1
0
forked from baron/baron-sso

perf(admin): implement server-side search and virtualization for tenant list

- Backend: Added 'search' parameter to TenantRepository and TenantService.
- Backend: Updated all Tenant list calls to support searching.
- Backend: Enhanced UserRepository.List to support cursor-based pagination and search.
- Frontend: Switched TenantListPage to use useInfiniteQuery for lazy loading.
- Frontend: Implemented list virtualization in TenantHierarchyView using @tanstack/react-virtual.
- Frontend: Added server-side search with debouncing (useDeferredValue).
- Fixed various Go compilation errors caused by method signature changes.
This commit is contained in:
2026-06-04 14:08:55 +09:00
parent 8f2e351875
commit 6d3f128282
18 changed files with 223 additions and 108 deletions

View File

@@ -20,7 +20,7 @@ type TenantRepository interface {
FindByDomain(ctx context.Context, domainName string) (*domain.Tenant, error)
FindByIDs(ctx context.Context, ids []string) ([]domain.Tenant, error)
AddDomain(ctx context.Context, tenantID string, domainName string, verified bool) error
List(ctx context.Context, limit, offset int, parentID string) ([]domain.Tenant, int64, error)
List(ctx context.Context, limit, offset int, parentID string, search string) ([]domain.Tenant, int64, error)
ListByType(ctx context.Context, tenantType string) ([]domain.Tenant, error)
DeleteBulk(ctx context.Context, ids []string) error
}
@@ -124,7 +124,7 @@ func (r *tenantRepository) AddDomain(ctx context.Context, tenantID string, domai
return r.db.WithContext(ctx).Create(&td).Error
}
func (r *tenantRepository) List(ctx context.Context, limit, offset int, parentID string) ([]domain.Tenant, int64, error) {
func (r *tenantRepository) List(ctx context.Context, limit, offset int, parentID string, search string) ([]domain.Tenant, int64, error) {
var tenants []domain.Tenant
var total int64
db := r.db.WithContext(ctx).Model(&domain.Tenant{})
@@ -133,6 +133,11 @@ func (r *tenantRepository) List(ctx context.Context, limit, offset int, parentID
db = db.Where("parent_id = ?", parentID)
}
if search != "" {
searchTerm := "%" + strings.ToLower(search) + "%"
db = db.Where("LOWER(name) LIKE ? OR LOWER(slug) LIKE ? OR LOWER(description) LIKE ?", searchTerm, searchTerm, searchTerm)
}
if err := db.Count(&total).Error; err != nil {
return nil, 0, err
}

View File

@@ -2,6 +2,7 @@ package repository
import (
"baron-sso-backend/internal/domain"
"baron-sso-backend/internal/pagination"
"context"
"fmt"
"strings"
@@ -17,7 +18,7 @@ type UserRepository interface {
FindByID(ctx context.Context, id string) (*domain.User, error)
FindByIDs(ctx context.Context, ids []string) ([]domain.User, error)
ListByTenant(ctx context.Context, tenantID string) ([]domain.User, error)
List(ctx context.Context, offset, limit int, search string, tenantSlug string) ([]domain.User, int64, error)
List(ctx context.Context, offset, limit int, search string, tenantSlug string, cursor string) ([]domain.User, int64, string, error)
CountByTenant(ctx context.Context, tenantID string) (int64, error)
CountByTenantIDs(ctx context.Context, tenantIDs []string) (map[string]int64, error)
CountByCompanyCodes(ctx context.Context, codes []string) (map[string]int64, error)
@@ -215,7 +216,7 @@ func lowerStrings(arr []string) []string {
return res
}
func (r *userRepository) List(ctx context.Context, offset, limit int, search string, tenantSlug string) ([]domain.User, int64, error) {
func (r *userRepository) List(ctx context.Context, offset, limit int, search string, tenantSlug string, cursorRaw string) ([]domain.User, int64, string, error) {
var users []domain.User
var total int64
db := r.db.WithContext(ctx).Model(&domain.User{})
@@ -232,14 +233,34 @@ func (r *userRepository) List(ctx context.Context, offset, limit int, search str
}
if err := db.Count(&total).Error; err != nil {
return nil, 0, err
return nil, 0, "", err
}
if err := db.Offset(offset).Limit(limit).Preload("Tenant").Find(&users).Error; err != nil {
return nil, 0, err
if cursorRaw != "" {
cursor, err := pagination.Decode(cursorRaw)
if err != nil {
return nil, 0, "", err
}
db = pagination.ApplyCreatedAtIDCursor(db, cursor, "created_at", "id")
} else {
db = db.Offset(offset)
}
return users, total, nil
if err := db.Order("created_at desc, id desc").Limit(limit + 1).Preload("Tenant").Find(&users).Error; err != nil {
return nil, 0, "", err
}
var items []domain.User
var nextCursor string
if len(users) > limit {
items = users[:limit]
last := items[limit-1]
nextCursor = pagination.Encode(last.CreatedAt, last.ID)
} else {
items = users
}
return items, total, nextCursor, nil
}
func (r *userRepository) Delete(ctx context.Context, id string) error {