diff --git a/frontend/src/lib/components/neurons/NnsNeuronsMissingRewardsBadge.svelte b/frontend/src/lib/components/neurons/NnsNeuronsMissingRewardsBadge.svelte
new file mode 100644
index 0000000000..6f1ff4f03f
--- /dev/null
+++ b/frontend/src/lib/components/neurons/NnsNeuronsMissingRewardsBadge.svelte
@@ -0,0 +1,32 @@
+
+
+
+ {#if $soonLosingRewardNeuronsStore.length > 0}
+
+ {/if}
+
+
+
diff --git a/frontend/src/lib/i18n/en.json b/frontend/src/lib/i18n/en.json
index 376ae424e7..18d43d6b05 100644
--- a/frontend/src/lib/i18n/en.json
+++ b/frontend/src/lib/i18n/en.json
@@ -394,6 +394,7 @@
"description": "ICP neurons that are inactive for $period start missing voting rewards. To avoid missing rewards, vote manually, edit, or confirm your following.",
"confirming": "Confirming following. This may take a moment.",
"confirm": "Confirm Following",
+ "badge_label": "Confirm following to avoid missing rewards",
"hw_hotkey_warning": "You may have neurons that are not added to the NNS dapp. If you want to view your neurons and get alerts in case they start missing voting rewards, you can add them by clicking Show Neurons > Add to NNS Dapp."
},
"losing_rewards_banner": {
diff --git a/frontend/src/lib/types/i18n.d.ts b/frontend/src/lib/types/i18n.d.ts
index d133c8809c..3c6be6f97f 100644
--- a/frontend/src/lib/types/i18n.d.ts
+++ b/frontend/src/lib/types/i18n.d.ts
@@ -408,6 +408,7 @@ interface I18nLosing_rewards {
description: string;
confirming: string;
confirm: string;
+ badge_label: string;
hw_hotkey_warning: string;
}
diff --git a/frontend/src/tests/lib/components/neurons/NnsNeuronsMissingRewardsBadge.spec.ts b/frontend/src/tests/lib/components/neurons/NnsNeuronsMissingRewardsBadge.spec.ts
new file mode 100644
index 0000000000..d5bb2ea8c0
--- /dev/null
+++ b/frontend/src/tests/lib/components/neurons/NnsNeuronsMissingRewardsBadge.spec.ts
@@ -0,0 +1,75 @@
+import NnsNeuronsMissingRewardsBadge from "$lib/components/neurons/NnsNeuronsMissingRewardsBadge.svelte";
+import { SECONDS_IN_HALF_YEAR } from "$lib/constants/constants";
+import { neuronsStore } from "$lib/stores/neurons.store";
+import { nowInSeconds } from "$lib/utils/date.utils";
+import { mockIdentity } from "$tests/mocks/auth.store.mock";
+import { mockFullNeuron, mockNeuron } from "$tests/mocks/neurons.mock";
+import { NnsNeuronsMissingRewardsBadgePo } from "$tests/page-objects/NnsNeuronsMissingRewardsBadge.page-object";
+import { JestPageObjectElement } from "$tests/page-objects/jest.page-object";
+import { render } from "$tests/utils/svelte.test-utils";
+
+describe("NnsNeuronsMissingRewardsBadge", () => {
+ const nowSeconds = nowInSeconds();
+ const activeNeuron = {
+ ...mockNeuron,
+ neuronId: 0n,
+ fullNeuron: {
+ ...mockFullNeuron,
+ votingPowerRefreshedTimestampSeconds: BigInt(nowSeconds),
+ controller: mockIdentity.getPrincipal().toText(),
+ },
+ };
+ const losingRewardsNeuron = {
+ ...mockNeuron,
+ neuronId: 2n,
+ fullNeuron: {
+ ...mockFullNeuron,
+ votingPowerRefreshedTimestampSeconds: BigInt(
+ nowSeconds - SECONDS_IN_HALF_YEAR
+ ),
+ controller: mockIdentity.getPrincipal().toText(),
+ },
+ };
+ const renderComponent = async () => {
+ const { container } = render(NnsNeuronsMissingRewardsBadge);
+ return NnsNeuronsMissingRewardsBadgePo.under(
+ new JestPageObjectElement(container)
+ );
+ };
+
+ beforeEach(() => {
+ vi.useFakeTimers({
+ now: nowSeconds * 1000,
+ });
+ });
+
+ it("should be visible when there is an inactive neuron", async () => {
+ neuronsStore.setNeurons({
+ neurons: [activeNeuron, losingRewardsNeuron],
+ certified: true,
+ });
+ const po = await renderComponent();
+
+ expect(await po.isVisible()).toBe(true);
+ });
+
+ it("should be hidden when there is no inactive neurons", async () => {
+ neuronsStore.setNeurons({
+ neurons: [activeNeuron],
+ certified: true,
+ });
+ const po = await renderComponent();
+
+ expect(await po.isVisible()).toBe(false);
+ });
+
+ it("should be hidden when there is no neurons", async () => {
+ neuronsStore.setNeurons({
+ neurons: [],
+ certified: true,
+ });
+ const po = await renderComponent();
+
+ expect(await po.isVisible()).toBe(false);
+ });
+});
diff --git a/frontend/src/tests/page-objects/NnsNeuronsMissingRewardsBadge.page-object.ts b/frontend/src/tests/page-objects/NnsNeuronsMissingRewardsBadge.page-object.ts
new file mode 100644
index 0000000000..b2df3bafd0
--- /dev/null
+++ b/frontend/src/tests/page-objects/NnsNeuronsMissingRewardsBadge.page-object.ts
@@ -0,0 +1,16 @@
+import { BasePageObject } from "$tests/page-objects/base.page-object";
+import type { PageObjectElement } from "$tests/types/page-object.types";
+
+export class NnsNeuronsMissingRewardsBadgePo extends BasePageObject {
+ private static readonly TID = "nns-neurons-missing-rewards-badge-component";
+
+ static under(element: PageObjectElement): NnsNeuronsMissingRewardsBadgePo {
+ return new NnsNeuronsMissingRewardsBadgePo(
+ element.byTestId(NnsNeuronsMissingRewardsBadgePo.TID)
+ );
+ }
+
+ isVisible(): Promise {
+ return this.root.byTestId("badge").isPresent();
+ }
+}