From f5f4447d60661aa92c80b50a16e8cb194dfa161f Mon Sep 17 00:00:00 2001 From: Sergey Krylov Date: Wed, 30 Sep 2026 07:40:43 +0300 Subject: [PATCH] fix: sort stock and bond events by payment date --- src/pages/bond/ui/BondPage/BondPage.vm.ts | 10 +++------- src/pages/stock/ui/StockPage/StockPage.vm.ts | 10 +++------- src/shared/lib/calendar.test.ts | 20 +++++++++++++++++++- src/shared/lib/calendar.ts | 8 +++++++- src/shared/lib/index.ts | 2 +- 5 files changed, 33 insertions(+), 17 deletions(-) diff --git a/src/pages/bond/ui/BondPage/BondPage.vm.ts b/src/pages/bond/ui/BondPage/BondPage.vm.ts index 7279927..ffa6c18 100644 --- a/src/pages/bond/ui/BondPage/BondPage.vm.ts +++ b/src/pages/bond/ui/BondPage/BondPage.vm.ts @@ -12,7 +12,7 @@ import { useBrokerageOperationHistory } from "@/entities/broker-operation"; import { section, useInstrumentParameters } from "@/features/instrument-details"; import { useScreenSync } from "@/features/tbank-connection"; import { apiErrorCode } from "@/shared/api"; -import { instrumentLogoUrl } from "@/shared/lib"; +import { compareDateDesc, instrumentLogoUrl } from "@/shared/lib"; type BondPageVm = ReturnType; @@ -92,9 +92,7 @@ function useBondPageVm() { coupons.data?.coupons .filter(event => event.timing !== "upcoming") .toSorted((a, b) => - (b.paymentDate ?? b.recordDate ?? "").localeCompare( - a.paymentDate ?? a.recordDate ?? "", - ), + compareDateDesc(a.paymentDate, b.paymentDate), ) ?? [], period, position: position.data, @@ -110,9 +108,7 @@ function useBondPageVm() { coupons.data?.coupons .filter(event => event.timing === "upcoming") .toSorted((a, b) => - (a.paymentDate ?? a.recordDate ?? "").localeCompare( - b.paymentDate ?? b.recordDate ?? "", - ), + compareDateDesc(b.paymentDate, a.paymentDate), ) ?? [], }; } diff --git a/src/pages/stock/ui/StockPage/StockPage.vm.ts b/src/pages/stock/ui/StockPage/StockPage.vm.ts index 9e416ee..98c7f19 100644 --- a/src/pages/stock/ui/StockPage/StockPage.vm.ts +++ b/src/pages/stock/ui/StockPage/StockPage.vm.ts @@ -15,7 +15,7 @@ import { } from "@/features/instrument-details"; import { useScreenSync } from "@/features/tbank-connection"; import { apiErrorCode } from "@/shared/api"; -import { instrumentLogoUrl } from "@/shared/lib"; +import { compareDateDesc, instrumentLogoUrl } from "@/shared/lib"; type StockPageVm = ReturnType; @@ -93,9 +93,7 @@ function useStockPageVm() { dividends.data?.dividends .filter(event => event.timing !== "upcoming") .toSorted((a, b) => - (b.recordDate ?? b.paymentDate ?? "").localeCompare( - a.recordDate ?? a.paymentDate ?? "", - ), + compareDateDesc(a.paymentDate, b.paymentDate), ) ?? [], period, position: position.data, @@ -113,9 +111,7 @@ function useStockPageVm() { dividends.data?.dividends .filter(event => event.timing === "upcoming") .toSorted((a, b) => - (a.recordDate ?? a.paymentDate ?? "").localeCompare( - b.recordDate ?? b.paymentDate ?? "", - ), + compareDateDesc(b.paymentDate, a.paymentDate), ) ?? [], }; } diff --git a/src/shared/lib/calendar.test.ts b/src/shared/lib/calendar.test.ts index ae8a521..d27959b 100644 --- a/src/shared/lib/calendar.test.ts +++ b/src/shared/lib/calendar.test.ts @@ -1,6 +1,6 @@ import { describe, expect, it } from "vitest"; -import { isValidPeriod, moscowDate } from "./calendar"; +import { compareDateDesc, isValidPeriod, moscowDate } from "./calendar"; import { calendarDate } from "./calendar.schema"; describe("calendar dates", () => { @@ -32,4 +32,22 @@ describe("calendar dates", () => { expect(moscowDate(new Date("2026-12-31T20:59:59Z"))).toBe("2026-12-31"); expect(moscowDate(new Date("2026-12-31T21:00:00Z"))).toBe("2027-01-01"); }); + + it("compares dates newest first and keeps dateless rows last", () => { + const dates = [null, "2026-01-01", "2026-03-01", "2026-02-01"]; + + expect(dates.toSorted(compareDateDesc)).toEqual([ + "2026-03-01", + "2026-02-01", + "2026-01-01", + null, + ]); + + expect(dates.toSorted((a, b) => compareDateDesc(b, a))).toEqual([ + null, + "2026-01-01", + "2026-02-01", + "2026-03-01", + ]); + }); }); diff --git a/src/shared/lib/calendar.ts b/src/shared/lib/calendar.ts index 552f89c..c3697b3 100644 --- a/src/shared/lib/calendar.ts +++ b/src/shared/lib/calendar.ts @@ -16,6 +16,12 @@ const moscowFormatter = new Intl.DateTimeFormat("en-CA", { year: "numeric", }); +// Newest first. Missing dates sort last in both directions, so a row without a +// date never jumps to the top of a "past" list. +function compareDateDesc(a: null | string, b: null | string) { + return (b ?? "").localeCompare(a ?? ""); +} + function moscowDate(value: Date): string { const parts = moscowFormatter.formatToParts(value); @@ -25,4 +31,4 @@ function moscowDate(value: Date): string { return `${part("year")}-${part("month")}-${part("day")}`; } -export { isValidPeriod, moscowDate }; +export { compareDateDesc, isValidPeriod, moscowDate }; diff --git a/src/shared/lib/index.ts b/src/shared/lib/index.ts index 9931999..5561391 100644 --- a/src/shared/lib/index.ts +++ b/src/shared/lib/index.ts @@ -1,4 +1,4 @@ -export { isValidPeriod, moscowDate } from "./calendar"; +export { compareDateDesc, isValidPeriod, moscowDate } from "./calendar"; export { calendarDate } from "./calendar.schema"; export { formatChartValue,