fix(frontend): add cooldown tooltips and fix settings tab initial load
Add AppHelpHint tooltips to ScrapeSafetyBadge and ScrapeSafetyBanner showing rate-limit explanation, cooldown reason, and recommended action. When no cooldown is active, show a simple "ready" message. Fix settings tabs not loading on initial navigation by deferring onMounted load via nextTick, and using flush:'post' on the section watcher so template refs are attached before load() runs.
This commit is contained in:
parent
39c7eb686c
commit
c85fa08b7b
5 changed files with 194 additions and 6 deletions
94
frontend/src/features/settings/SettingsAdminPanel.test.ts
Normal file
94
frontend/src/features/settings/SettingsAdminPanel.test.ts
Normal file
|
|
@ -0,0 +1,94 @@
|
|||
// @vitest-environment happy-dom
|
||||
import { describe, expect, it, vi, beforeEach } from "vitest";
|
||||
import { mount, flushPromises } from "@vue/test-utils";
|
||||
import { nextTick } from "vue";
|
||||
import SettingsAdminPanel from "./SettingsAdminPanel.vue";
|
||||
|
||||
vi.mock("@/features/admin_users", () => ({
|
||||
listAdminUsers: vi.fn().mockResolvedValue([]),
|
||||
createAdminUser: vi.fn(),
|
||||
setAdminUserActive: vi.fn(),
|
||||
resetAdminUserPassword: vi.fn(),
|
||||
}));
|
||||
|
||||
vi.mock("@/features/admin_dbops", () => ({
|
||||
getAdminDbIntegrityReport: vi.fn().mockResolvedValue({
|
||||
status: "ok",
|
||||
warnings: [],
|
||||
failures: [],
|
||||
checked_at: null,
|
||||
checks: [],
|
||||
}),
|
||||
listAdminPdfQueue: vi.fn().mockResolvedValue({
|
||||
items: [],
|
||||
total_count: 0,
|
||||
has_next: false,
|
||||
has_prev: false,
|
||||
page: 1,
|
||||
page_size: 50,
|
||||
}),
|
||||
requeueAdminPdfLookup: vi.fn(),
|
||||
requeueAllAdminPdfLookups: vi.fn(),
|
||||
}));
|
||||
|
||||
vi.mock("@/stores/auth", () => ({
|
||||
useAuthStore: () => ({ isAdmin: true }),
|
||||
}));
|
||||
|
||||
vi.mock("@/features/admin_repairs", () => ({
|
||||
listAdminRepairTasks: vi.fn().mockResolvedValue([]),
|
||||
runAdminRepairTask: vi.fn(),
|
||||
}));
|
||||
|
||||
import { listAdminUsers } from "@/features/admin_users";
|
||||
import { getAdminDbIntegrityReport, listAdminPdfQueue } from "@/features/admin_dbops";
|
||||
|
||||
const mockedListUsers = vi.mocked(listAdminUsers);
|
||||
const mockedGetReport = vi.mocked(getAdminDbIntegrityReport);
|
||||
const mockedListPdfQueue = vi.mocked(listAdminPdfQueue);
|
||||
|
||||
describe("SettingsAdminPanel", () => {
|
||||
beforeEach(() => {
|
||||
mockedListUsers.mockClear();
|
||||
mockedGetReport.mockClear();
|
||||
mockedListPdfQueue.mockClear();
|
||||
});
|
||||
|
||||
it("calls load on users section after nextTick when section=users", async () => {
|
||||
mount(SettingsAdminPanel, { props: { section: "users" } });
|
||||
await nextTick();
|
||||
await nextTick();
|
||||
await flushPromises();
|
||||
expect(mockedListUsers).toHaveBeenCalled();
|
||||
});
|
||||
|
||||
it("calls load on integrity section after nextTick when section=integrity", async () => {
|
||||
mount(SettingsAdminPanel, { props: { section: "integrity" } });
|
||||
await nextTick();
|
||||
await nextTick();
|
||||
await flushPromises();
|
||||
expect(mockedGetReport).toHaveBeenCalled();
|
||||
});
|
||||
|
||||
it("calls load on new section when section prop changes", async () => {
|
||||
const wrapper = mount(SettingsAdminPanel, { props: { section: "users" } });
|
||||
await nextTick();
|
||||
await nextTick();
|
||||
await flushPromises();
|
||||
mockedListUsers.mockClear();
|
||||
mockedGetReport.mockClear();
|
||||
|
||||
await wrapper.setProps({ section: "integrity" });
|
||||
await nextTick();
|
||||
await flushPromises();
|
||||
expect(mockedGetReport).toHaveBeenCalled();
|
||||
});
|
||||
|
||||
it("calls load on pdf section when section=pdf", async () => {
|
||||
mount(SettingsAdminPanel, { props: { section: "pdf" } });
|
||||
await nextTick();
|
||||
await nextTick();
|
||||
await flushPromises();
|
||||
expect(mockedListPdfQueue).toHaveBeenCalled();
|
||||
});
|
||||
});
|
||||
|
|
@ -1,5 +1,5 @@
|
|||
<script setup lang="ts">
|
||||
import { onMounted, watch } from "vue";
|
||||
import { nextTick, onMounted, watch } from "vue";
|
||||
import { ref } from "vue";
|
||||
|
||||
import RequestStateAlerts from "@/components/patterns/RequestStateAlerts.vue";
|
||||
|
|
@ -43,8 +43,10 @@ async function loadSection(): Promise<void> {
|
|||
}
|
||||
}
|
||||
|
||||
onMounted(loadSection);
|
||||
watch(() => props.section, loadSection);
|
||||
onMounted(() => {
|
||||
nextTick(loadSection);
|
||||
});
|
||||
watch(() => props.section, loadSection, { flush: "post" });
|
||||
</script>
|
||||
|
||||
<template>
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue