1
0
forked from baron/baron-sso

perf(admin): full-stack performance optimization for all list tables

- Implemented server-side search, infinite scrolling, and list virtualization for Tenants, Users, and Audit Logs.
- Backend: Enhanced Repository, Service, and Handler layers to support 'search' and 'cursor' parameters.
- Frontend: Integrated @tanstack/react-virtual and useInfiniteQuery for high-performance rendering.
- Quality: Updated all unit tests and E2E tests to match the new asynchronous server-side search architecture.
- i18n: Synced all translation keys and cleaned up unused resources.
This commit is contained in:
2026-06-04 16:06:30 +09:00
parent 6d3f128282
commit b2f155e35b
26 changed files with 1103 additions and 440 deletions

View File

@@ -53,13 +53,33 @@ test.describe("Audit Logs Management", () => {
const url = route.request().url();
const urlObj = new URL(url);
const cursor = urlObj.searchParams.get("cursor");
const search = urlObj.searchParams.get("search")?.toLowerCase();
const status = urlObj.searchParams.get("status");
const offset = cursor ? 20 : 0;
console.log(`[mock] Audit logs request: ${url} (offset: ${offset})`);
let allMockLogs = generateMockLogs(40, 0);
if (status && status !== "all") {
allMockLogs = allMockLogs.filter((l) => l.status === status);
}
if (search) {
allMockLogs = allMockLogs.filter(
(l) =>
l.user_id.toLowerCase().includes(search) ||
l.details.toLowerCase().includes(search),
);
}
const paginatedItems = allMockLogs.slice(offset, offset + 20);
console.log(
`[mock] Audit logs request: ${url} (offset: ${offset}, search: ${search}, status: ${status}, results: ${paginatedItems.length})`,
);
return route.fulfill({
json: {
items: generateMockLogs(20, offset),
next_cursor: offset === 0 ? "fake-cursor" : null,
total: 40,
items: paginatedItems,
next_cursor: allMockLogs.length > offset + 20 ? "fake-cursor" : null,
total: allMockLogs.length,
},
headers: { "Access-Control-Allow-Origin": "*" },
});
@@ -172,7 +192,7 @@ test.describe("Audit Logs Management", () => {
await userIdInput.fill("user-even");
// Wait for deferred value to apply
await expect(page.locator("tbody tr")).toHaveCount(10, { timeout: 15000 });
await expect(page.locator("tbody tr")).toHaveCount(20, { timeout: 15000 });
await expect(page.locator("tbody")).not.toContainText("user-odd");
// Clear User ID
@@ -183,12 +203,13 @@ test.describe("Audit Logs Management", () => {
const actionInput = page.getByTestId("audit-search-action");
await actionInput.fill("ROTATE_SECRET");
// Check that we only see ROTATE_SECRET (20 - 7 = 13)
await expect(page.locator("tbody tr")).toHaveCount(13, { timeout: 15000 });
// Check that we see ROTATE_SECRET across all 40 logs (40 - 14 = 26)
// Wait for the mock to respond and render
await expect(page.locator("tbody tr")).toHaveCount(20, { timeout: 15000 });
await expect(page.locator("tbody")).not.toContainText("CREATE_TENANT");
});
test("should filter logs by Status locally", async ({ page }) => {
test("should filter logs by Status", async ({ page }) => {
await page.goto("/audit-logs");
await expect(page.locator(".animate-spin")).not.toBeVisible({
timeout: 10000,
@@ -201,12 +222,13 @@ test.describe("Audit Logs Management", () => {
// Select "Failure" status
await page.getByTestId("audit-filter-status").selectOption("failure");
// ID % 5 === 0 are status "failure" (0, 5, 10, 15)
await expect(page.locator("tbody tr")).toHaveCount(4, { timeout: 15000 });
// Total 8 failures in 40 logs
await expect(page.locator("tbody tr")).toHaveCount(8, { timeout: 15000 });
// Select "Success" status
await page.getByTestId("audit-filter-status").selectOption("success");
await expect(page.locator("tbody tr")).toHaveCount(16, { timeout: 15000 });
// Total 32 successes in 40 logs, but page limit is 20
await expect(page.locator("tbody tr")).toHaveCount(20, { timeout: 15000 });
});
});