feat(admin-ui): поиск справочников + счётчик активных записей
Backend: - DictionaryRecordRepository.countActiveGroupedByDictionary — один GROUP BY по всем справочникам с фильтром по scope, без N+1. - DictionaryResponse.recordCount (nullable) + factory from(d, count). - DictionaryDefinitionController передаёт счётчики в list/get, фильтрованные по currentScopes() пользователя. Frontend: - SearchInput над сеткой карточек, client-side filter по name/displayName/description (case-insensitive, useDeferredValue). - Badge "N записей" (i18n plural ru/en) в углу карточки. - "Показано N из M" рядом с поиском. - EmptyState когда поиск пустой.
This commit is contained in:
+6
@@ -21,12 +21,17 @@ public record DictionaryResponse(
|
||||
String bundle,
|
||||
List<String> supportedLocales,
|
||||
String defaultLocale,
|
||||
Long recordCount,
|
||||
OffsetDateTime createdAt,
|
||||
OffsetDateTime updatedAt,
|
||||
String createdBy,
|
||||
String updatedBy) {
|
||||
|
||||
public static DictionaryResponse from(DictionaryDefinition d) {
|
||||
return from(d, null);
|
||||
}
|
||||
|
||||
public static DictionaryResponse from(DictionaryDefinition d, Long recordCount) {
|
||||
return new DictionaryResponse(
|
||||
d.getId(),
|
||||
d.getName(),
|
||||
@@ -38,6 +43,7 @@ public record DictionaryResponse(
|
||||
d.getBundle(),
|
||||
d.getSupportedLocales() == null ? List.of() : List.of(d.getSupportedLocales()),
|
||||
d.getDefaultLocale(),
|
||||
recordCount,
|
||||
d.getCreatedAt(),
|
||||
d.getUpdatedAt(),
|
||||
d.getCreatedBy(),
|
||||
|
||||
+25
-3
@@ -2,6 +2,7 @@ package cloud.nstart.terravault.ordinis.restapi.web;
|
||||
|
||||
import cloud.nstart.terravault.ordinis.auth.ScopeContext;
|
||||
import cloud.nstart.terravault.ordinis.domain.DataScope;
|
||||
import cloud.nstart.terravault.ordinis.domain.record.DictionaryRecordRepository;
|
||||
import cloud.nstart.terravault.ordinis.restapi.dto.CreateDictionaryRequest;
|
||||
import cloud.nstart.terravault.ordinis.restapi.dto.DictionaryResponse;
|
||||
import cloud.nstart.terravault.ordinis.restapi.error.OrdinisException;
|
||||
@@ -17,7 +18,12 @@ import org.springframework.web.bind.annotation.RequestMapping;
|
||||
import org.springframework.web.bind.annotation.ResponseStatus;
|
||||
import org.springframework.web.bind.annotation.RestController;
|
||||
|
||||
import java.time.OffsetDateTime;
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
import java.util.UUID;
|
||||
|
||||
@RestController
|
||||
@RequestMapping("/api/v1/dictionaries")
|
||||
@@ -25,18 +31,24 @@ public class DictionaryDefinitionController {
|
||||
|
||||
private final DictionaryDefinitionService service;
|
||||
private final ScopeContext scopeContext;
|
||||
private final DictionaryRecordRepository recordRepository;
|
||||
|
||||
public DictionaryDefinitionController(
|
||||
DictionaryDefinitionService service, ScopeContext scopeContext) {
|
||||
DictionaryDefinitionService service,
|
||||
ScopeContext scopeContext,
|
||||
DictionaryRecordRepository recordRepository) {
|
||||
this.service = service;
|
||||
this.scopeContext = scopeContext;
|
||||
this.recordRepository = recordRepository;
|
||||
}
|
||||
|
||||
@GetMapping
|
||||
public List<DictionaryResponse> list() {
|
||||
Set<DataScope> allowedScopes = scopeContext.currentScopes();
|
||||
Map<UUID, Long> counts = countsByDictionary(allowedScopes);
|
||||
return service.findAll().stream()
|
||||
.filter(d -> scopeContext.canAccess(d.getScope()))
|
||||
.map(DictionaryResponse::from)
|
||||
.map(d -> DictionaryResponse.from(d, counts.getOrDefault(d.getId(), 0L)))
|
||||
.toList();
|
||||
}
|
||||
|
||||
@@ -47,7 +59,17 @@ public class DictionaryDefinitionController {
|
||||
throw OrdinisException.notFound(
|
||||
"dictionary_not_found", "Dictionary not found: " + name);
|
||||
}
|
||||
return DictionaryResponse.from(d);
|
||||
Map<UUID, Long> counts = countsByDictionary(scopeContext.currentScopes());
|
||||
return DictionaryResponse.from(d, counts.getOrDefault(d.getId(), 0L));
|
||||
}
|
||||
|
||||
private Map<UUID, Long> countsByDictionary(Set<DataScope> allowedScopes) {
|
||||
Map<UUID, Long> counts = new HashMap<>();
|
||||
for (Object[] row :
|
||||
recordRepository.countActiveGroupedByDictionary(allowedScopes, OffsetDateTime.now())) {
|
||||
counts.put((UUID) row[0], (Long) row[1]);
|
||||
}
|
||||
return counts;
|
||||
}
|
||||
|
||||
@PostMapping
|
||||
|
||||
Reference in New Issue
Block a user