feat(tenancy): add merchant team CRUD with multi-store hub view
The merchant team page was read-only. Now merchant owners can invite, edit roles, and remove team members across all their stores from a single hub view. Architecture: No new models — delegates to existing store_team_service. Members are deduplicated across stores with per-store role badges. New: - 5 API endpoints: GET team (member-centric), GET store roles, POST invite (multi-store), PUT update role, DELETE remove member - merchant-team.js Alpine component with invite/edit/remove modals - Full CRUD template with stats cards, store filter, member table - 7 Pydantic schemas for merchant team request/response - 2 service methods: validate_store_ownership, get_merchant_team_members - 25 new i18n keys across 4 tenancy locales + 1 core common key Tests: 434 tenancy tests passing, arch-check green. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -12,20 +12,45 @@
|
||||
"team": "Team"
|
||||
},
|
||||
"team": {
|
||||
"title": "Team",
|
||||
"members": "Mitglieder",
|
||||
"actions": "Aktionen",
|
||||
"active": "Aktiv",
|
||||
"add_member": "Mitglied hinzufügen",
|
||||
"all_stores": "Alle Filialen",
|
||||
"edit_member": "Mitglied bearbeiten",
|
||||
"editor": "Bearbeiter",
|
||||
"email": "E-Mail",
|
||||
"email_placeholder": "E-Mail-Adresse eingeben",
|
||||
"error_title": "Fehler beim Laden des Teams",
|
||||
"first_name": "Vorname",
|
||||
"invite_first_member": "Laden Sie Ihr erstes Teammitglied ein",
|
||||
"invite_member": "Mitglied einladen",
|
||||
"invitation_accepted": "Einladung angenommen",
|
||||
"invitation_sent": "Einladung gesendet",
|
||||
"last_name": "Nachname",
|
||||
"loading_team": "Team wird geladen...",
|
||||
"manage_members_description": "Teammitglieder über alle Filialen verwalten",
|
||||
"manager": "Manager",
|
||||
"member": "Mitglied",
|
||||
"member_stores": "Filialen des Mitglieds",
|
||||
"members": "Mitglieder",
|
||||
"no_members_description": "Laden Sie Teammitglieder ein um Ihre Filialen zu verwalten",
|
||||
"no_members_title": "Noch keine Teammitglieder",
|
||||
"no_role": "Keine Rolle",
|
||||
"owner": "Inhaber",
|
||||
"pending_invitations": "Ausstehende Einladungen",
|
||||
"permissions": "Berechtigungen",
|
||||
"remove_confirmation": "Sind Sie sicher, dass Sie entfernen möchten",
|
||||
"remove_from_all_stores": "Aus allen Filialen entfernen",
|
||||
"remove_member": "Mitglied entfernen",
|
||||
"role": "Rolle",
|
||||
"owner": "Inhaber",
|
||||
"manager": "Manager",
|
||||
"editor": "Bearbeiter",
|
||||
"viewer": "Betrachter",
|
||||
"permissions": "Berechtigungen",
|
||||
"pending_invitations": "Ausstehende Einladungen",
|
||||
"invitation_sent": "Einladung gesendet",
|
||||
"invitation_accepted": "Einladung angenommen"
|
||||
"select_stores": "Filialen auswählen",
|
||||
"send_invitation": "Einladung senden",
|
||||
"status": "Status",
|
||||
"store_roles": "Filialrollen",
|
||||
"stores_and_roles": "Filialen & Rollen",
|
||||
"title": "Team",
|
||||
"total_members": "Mitglieder gesamt",
|
||||
"viewer": "Betrachter"
|
||||
},
|
||||
"messages": {
|
||||
"business_info_saved": "Business info saved",
|
||||
|
||||
@@ -12,20 +12,45 @@
|
||||
"team": "Team"
|
||||
},
|
||||
"team": {
|
||||
"title": "Team",
|
||||
"members": "Members",
|
||||
"actions": "Actions",
|
||||
"active": "Active",
|
||||
"add_member": "Add Member",
|
||||
"all_stores": "All Stores",
|
||||
"edit_member": "Edit Member",
|
||||
"editor": "Editor",
|
||||
"email": "Email",
|
||||
"email_placeholder": "Enter email address",
|
||||
"error_title": "Error loading team",
|
||||
"first_name": "First Name",
|
||||
"invite_first_member": "Invite your first team member",
|
||||
"invite_member": "Invite Member",
|
||||
"invitation_accepted": "Invitation Accepted",
|
||||
"invitation_sent": "Invitation Sent",
|
||||
"last_name": "Last Name",
|
||||
"loading_team": "Loading team...",
|
||||
"manage_members_description": "Manage team members across all your stores",
|
||||
"manager": "Manager",
|
||||
"member": "Member",
|
||||
"member_stores": "Member's stores",
|
||||
"members": "Members",
|
||||
"no_members_description": "Invite team members to help manage your stores",
|
||||
"no_members_title": "No team members yet",
|
||||
"no_role": "No role",
|
||||
"owner": "Owner",
|
||||
"pending_invitations": "Pending Invitations",
|
||||
"permissions": "Permissions",
|
||||
"remove_confirmation": "Are you sure you want to remove",
|
||||
"remove_from_all_stores": "Remove from all stores",
|
||||
"remove_member": "Remove Member",
|
||||
"role": "Role",
|
||||
"owner": "Owner",
|
||||
"manager": "Manager",
|
||||
"editor": "Editor",
|
||||
"viewer": "Viewer",
|
||||
"permissions": "Permissions",
|
||||
"pending_invitations": "Pending Invitations",
|
||||
"invitation_sent": "Invitation Sent",
|
||||
"invitation_accepted": "Invitation Accepted"
|
||||
"select_stores": "Select Stores",
|
||||
"send_invitation": "Send Invitation",
|
||||
"status": "Status",
|
||||
"store_roles": "Store Roles",
|
||||
"stores_and_roles": "Stores & Roles",
|
||||
"title": "Team",
|
||||
"total_members": "Total Members",
|
||||
"viewer": "Viewer"
|
||||
},
|
||||
"messages": {
|
||||
"business_info_saved": "Business info saved",
|
||||
|
||||
@@ -12,20 +12,45 @@
|
||||
"team": "Équipe"
|
||||
},
|
||||
"team": {
|
||||
"title": "Équipe",
|
||||
"members": "Membres",
|
||||
"actions": "Actions",
|
||||
"active": "Actifs",
|
||||
"add_member": "Ajouter un membre",
|
||||
"all_stores": "Tous les magasins",
|
||||
"edit_member": "Modifier le membre",
|
||||
"editor": "Éditeur",
|
||||
"email": "E-mail",
|
||||
"email_placeholder": "Saisir l'adresse e-mail",
|
||||
"error_title": "Erreur lors du chargement",
|
||||
"first_name": "Prénom",
|
||||
"invite_first_member": "Invitez votre premier membre",
|
||||
"invite_member": "Inviter un membre",
|
||||
"invitation_accepted": "Invitation acceptée",
|
||||
"invitation_sent": "Invitation envoyée",
|
||||
"last_name": "Nom de famille",
|
||||
"loading_team": "Chargement de l'équipe...",
|
||||
"manage_members_description": "Gérer les membres de l'équipe sur tous vos magasins",
|
||||
"manager": "Gestionnaire",
|
||||
"member": "Membre",
|
||||
"member_stores": "Magasins du membre",
|
||||
"members": "Membres",
|
||||
"no_members_description": "Invitez des membres pour gérer vos magasins",
|
||||
"no_members_title": "Aucun membre encore",
|
||||
"no_role": "Aucun rôle",
|
||||
"owner": "Propriétaire",
|
||||
"pending_invitations": "Invitations en attente",
|
||||
"permissions": "Permissions",
|
||||
"remove_confirmation": "Êtes-vous sûr de vouloir supprimer",
|
||||
"remove_from_all_stores": "Supprimer de tous les magasins",
|
||||
"remove_member": "Retirer un membre",
|
||||
"role": "Rôle",
|
||||
"owner": "Propriétaire",
|
||||
"manager": "Gestionnaire",
|
||||
"editor": "Éditeur",
|
||||
"viewer": "Lecteur",
|
||||
"permissions": "Permissions",
|
||||
"pending_invitations": "Invitations en attente",
|
||||
"invitation_sent": "Invitation envoyée",
|
||||
"invitation_accepted": "Invitation acceptée"
|
||||
"select_stores": "Sélectionner les magasins",
|
||||
"send_invitation": "Envoyer l'invitation",
|
||||
"status": "Statut",
|
||||
"store_roles": "Rôles par magasin",
|
||||
"stores_and_roles": "Magasins et rôles",
|
||||
"title": "Équipe",
|
||||
"total_members": "Membres totaux",
|
||||
"viewer": "Lecteur"
|
||||
},
|
||||
"messages": {
|
||||
"business_info_saved": "Business info saved",
|
||||
|
||||
@@ -12,20 +12,45 @@
|
||||
"team": "Team"
|
||||
},
|
||||
"team": {
|
||||
"title": "Team",
|
||||
"members": "Memberen",
|
||||
"actions": "Aktiounen",
|
||||
"active": "Aktiv",
|
||||
"add_member": "Member derbäisetzen",
|
||||
"all_stores": "All Geschäfter",
|
||||
"edit_member": "Member änneren",
|
||||
"editor": "Editeur",
|
||||
"email": "E-Mail",
|
||||
"email_placeholder": "E-Mail-Adress aginn",
|
||||
"error_title": "Feeler beim Lueden vum Team",
|
||||
"first_name": "Virnumm",
|
||||
"invite_first_member": "Invitéiert Äert éischt Teammember",
|
||||
"invite_member": "Member invitéieren",
|
||||
"invitation_accepted": "Invitatioun ugeholl",
|
||||
"invitation_sent": "Invitatioun geschéckt",
|
||||
"last_name": "Nonumm",
|
||||
"loading_team": "Team gëtt gelueden...",
|
||||
"manage_members_description": "Teammemberen iwwer all Geschäfter verwalten",
|
||||
"manager": "Manager",
|
||||
"member": "Member",
|
||||
"member_stores": "Geschäfter vum Member",
|
||||
"members": "Memberen",
|
||||
"no_members_description": "Invitéiert Teammemberen fir Är Geschäfter ze verwalten",
|
||||
"no_members_title": "Nach keng Teammemberen",
|
||||
"no_role": "Keng Roll",
|
||||
"owner": "Proprietär",
|
||||
"pending_invitations": "Aussteesend Invitatiounen",
|
||||
"permissions": "Rechter",
|
||||
"remove_confirmation": "Sidd Dir sécher, datt Dir ewechhuele wëllt",
|
||||
"remove_from_all_stores": "Vun all Geschäfter ewechhuelen",
|
||||
"remove_member": "Member ewechhuelen",
|
||||
"role": "Roll",
|
||||
"owner": "Proprietär",
|
||||
"manager": "Manager",
|
||||
"editor": "Editeur",
|
||||
"viewer": "Betruechter",
|
||||
"permissions": "Rechter",
|
||||
"pending_invitations": "Aussteesend Invitatiounen",
|
||||
"invitation_sent": "Invitatioun geschéckt",
|
||||
"invitation_accepted": "Invitatioun ugeholl"
|
||||
"select_stores": "Geschäfter wielen",
|
||||
"send_invitation": "Invitatioun schécken",
|
||||
"status": "Status",
|
||||
"store_roles": "Geschäftsrollen",
|
||||
"stores_and_roles": "Geschäfter & Rollen",
|
||||
"title": "Team",
|
||||
"total_members": "Memberen total",
|
||||
"viewer": "Betruechter"
|
||||
},
|
||||
"messages": {
|
||||
"business_info_saved": "Business info saved",
|
||||
|
||||
Reference in New Issue
Block a user