fix(autoobserve): keep See all reachable when decided suggestions empty the rail page

The rail decides whether more suggestions exist from the server total, not the page length. An empty page with a non-zero total keeps the count and See all, and says the top suggestions were already handled instead of claiming there are none.
This commit is contained in:
Ashish Kolhe
2026-11-03 11:59:20 +05:30
parent 5bc9a9e8a0
commit 86f633f2df
3 changed files with 49 additions and 10 deletions
@@ -177,3 +177,26 @@ describe("LiveSuggestions rail (OSS-11, OSS-9)", () => {
expect(announce(wrapper).text()).toBe("Suggestions loaded");
});
});
describe("LiveSuggestions rail when decisions empty the first page", () => {
it("keeps See all from the server total, and never says there are no suggestions", () => {
const wrapper = mountRail(rail(0, 12));
expect(cards(wrapper)).toHaveLength(0);
const seeAll = wrapper.find('[data-test="autoobserve2-suggestions-see-all"]');
expect(seeAll.exists()).toBe(true);
expect(seeAll.text()).toBe("See all 12");
expect(wrapper.find('[data-test="autoobserve2-suggestions-more"]').exists()).toBe(false);
expect(wrapper.find('[data-test="autoobserve2-suggestions-empty"]').exists()).toBe(false);
expect(wrapper.find('[data-test="autoobserve2-suggestions-page-empty"]').text()).toContain(
"The top suggestions were already handled",
);
expect(countChip(wrapper)).toBe("12");
});
it("keeps See all when the page holds fewer cards than the total", () => {
const wrapper = mountRail(rail(2, 12));
expect(cards(wrapper)).toHaveLength(2);
expect(wrapper.find('[data-test="autoobserve2-suggestions-more"]').text()).toBe("and 10 more");
expect(wrapper.find('[data-test="autoobserve2-suggestions-see-all"]').exists()).toBe(true);
});
});
@@ -17,7 +17,7 @@ along with this program. If not, see <http://www.gnu.org/licenses/>.
<template>
<SectionCard
:title="t('autoobserve2.suggestions.title')"
:count="listed ? rail.total : undefined"
:count="listed || hidden ? rail.total : undefined"
:aria-busy="rail.status === 'loading'"
data-test="autoobserve2-suggestions"
>
@@ -25,7 +25,7 @@ along with this program. If not, see <http://www.gnu.org/licenses/>.
loadText
}}</span>
<p
v-if="rail.status === 'loading' || skeleton || listed"
v-if="rail.status === 'loading' || skeleton || listed || hidden"
class="text-text-secondary -mt-2 px-5 pb-3 text-sm leading-relaxed text-pretty sm:px-6"
data-test="autoobserve2-suggestions-subtitle"
>
@@ -78,6 +78,15 @@ along with this program. If not, see <http://www.gnu.org/licenses/>.
@retry="rail.retry()"
/>
<OEmptyState
v-if="hidden"
size="inline"
icon="task-alt"
:title="t('autoobserveLive.notice.suggestions.pageEmptyTitle')"
:description="t('autoobserveLive.notice.suggestions.pageEmptyDesc')"
data-test="autoobserve2-suggestions-page-empty"
/>
<OEmptyState
v-else
size="inline"
icon="task-alt"
:title="t('autoobserveLive.notice.suggestions.emptyTitle')"
@@ -101,11 +110,14 @@ along with this program. If not, see <http://www.gnu.org/licenses/>.
</li>
</ul>
</template>
<template v-if="more" #footer>
<template v-if="beyond" #footer>
<div class="flex items-center gap-2">
<span class="text-text-secondary text-xs" data-test="autoobserve2-suggestions-more">{{
more
}}</span>
<span
v-if="more"
class="text-text-secondary text-xs"
data-test="autoobserve2-suggestions-more"
>{{ more }}</span
>
<OButton
variant="ghost-primary"
size="xs"
@@ -157,9 +169,11 @@ const emit = defineEmits<{ "see-all": [] }>();
const { t, locale } = useI18nTyped();
const shown = computed(() => props.rail.requests.slice(0, RAIL_CAP));
const listed = computed(
() => !props.skeleton && props.rail.status === "ready" && shown.value.length > 0,
);
const settled = computed(() => !props.skeleton && props.rail.status === "ready");
const listed = computed(() => settled.value && shown.value.length > 0);
// The server total, not this page's length, decides whether more exist: decided suggestions can empty a page.
const beyond = computed(() => settled.value && props.rail.total > shown.value.length);
const hidden = computed(() => beyond.value && !listed.value);
const notice = computed(() =>
props.rail.status === "error" && props.rail.failure
? liveNotice(t, "suggestions", props.rail.failure)
+3 -1
View File
@@ -27367,7 +27367,9 @@
"failedTitle": "Couldn't load suggestions",
"failedDesc": "Something went wrong while loading them. Try again in a moment.",
"emptyTitle": "No suggestions right now",
"emptyDesc": "I'll add one here when I find a gap in what your monitors cover."
"emptyDesc": "I'll add one here when I find a gap in what your monitors cover.",
"pageEmptyTitle": "The top suggestions were already handled",
"pageEmptyDesc": "Open the full list for the ones still waiting."
}
},
"time": {