forked from baron/baron-sso
feat: implement multi-tenant member management and UI improvements
- Add multi-tenant support (isAddTenant, isRemoveTenant) to backend UpdateUser API. - Update UserRepository to support searching in company_codes array. - Implement table sorting and align search bar layout in adminfront. - Add 'Assign Existing Member' and 'Exclude from Organization' features to TenantUsersPage. - Auto-populate tenantSlug in UserCreatePage via query parameters. - Add necessary localization keys for new UI elements. Resolves #644, #639, #642, #641
This commit is contained in:
@@ -5,6 +5,7 @@ import (
|
||||
"context"
|
||||
"strings"
|
||||
|
||||
"github.com/lib/pq"
|
||||
"gorm.io/gorm"
|
||||
"gorm.io/gorm/clause"
|
||||
)
|
||||
@@ -156,11 +157,12 @@ func (r *userRepository) CountByCompanyCodes(ctx context.Context, codes []string
|
||||
}
|
||||
var results []result
|
||||
|
||||
// Search by company_code directly. Normalize inputs using LOWER for robust matching.
|
||||
err := r.db.WithContext(ctx).Model(&domain.User{}).
|
||||
Select("LOWER(company_code) as company_code, count(*) as count").
|
||||
Where("LOWER(company_code) IN ?", lowerStrings(codes)).
|
||||
Group("LOWER(company_code)").
|
||||
// Search by company_codes array using unnest and overlap.
|
||||
// This ensures users with multiple memberships are counted in each tenant they belong to.
|
||||
err := r.db.WithContext(ctx).Table("users").
|
||||
Select("unnest(company_codes) as company_code, count(*) as count").
|
||||
Where("company_codes && ?", pq.Array(lowerStrings(codes))).
|
||||
Group("company_code").
|
||||
Scan(&results).Error
|
||||
if err != nil {
|
||||
return nil, err
|
||||
@@ -168,7 +170,7 @@ func (r *userRepository) CountByCompanyCodes(ctx context.Context, codes []string
|
||||
|
||||
counts := make(map[string]int64)
|
||||
for _, res := range results {
|
||||
counts[res.CompanyCode] = res.Count
|
||||
counts[strings.ToLower(res.CompanyCode)] = res.Count
|
||||
}
|
||||
|
||||
// Ensure all requested codes are present in results (even if count is 0)
|
||||
@@ -196,15 +198,15 @@ func (r *userRepository) List(ctx context.Context, offset, limit int, search str
|
||||
db := r.db.WithContext(ctx).Model(&domain.User{})
|
||||
|
||||
if companyCode != "" {
|
||||
// [Matrix Fix] Match users either by their primary company code OR by the slug of the department they are attached to
|
||||
// [Matrix Fix] Match users either by their primary company code OR by being in the company_codes array OR by tenant slug
|
||||
db = db.Joins("LEFT JOIN tenants ON users.tenant_id = tenants.id").
|
||||
Where("users.company_code = ? OR tenants.slug = ?", companyCode, companyCode)
|
||||
Where("users.company_code = ? OR ? = ANY(users.company_codes) OR tenants.slug = ?", companyCode, companyCode, companyCode)
|
||||
}
|
||||
|
||||
if search != "" {
|
||||
searchTerm := "%" + search + "%"
|
||||
db = db.Where("(users.email LIKE ? OR users.name LIKE ? OR users.company_code LIKE ? OR users.metadata::text LIKE ?)",
|
||||
searchTerm, searchTerm, searchTerm, searchTerm)
|
||||
db = db.Where("(users.email LIKE ? OR users.name LIKE ? OR users.company_code LIKE ? OR ? = ANY(users.company_codes) OR users.metadata::text LIKE ?)",
|
||||
searchTerm, searchTerm, searchTerm, search, searchTerm)
|
||||
}
|
||||
|
||||
if err := db.Count(&total).Error; err != nil {
|
||||
|
||||
Reference in New Issue
Block a user