Per-section history cap (8) + rename 'Вы искали' → 'Ранее искали' (TIRREDESIGN-5)
Angular keeps up to 8 items in each sidebar section (board / schedule / flight-number). We were capping the union at 10, which let a burst of flight-number lookups evict board-route entries. Split the cap by section so each bucket is independent. Label also moves from 'Вы искали' → 'Ранее искали' (en: 'Previous searches') per the redesign copy. Tests cover both the single-section cap and the independence invariant.
This commit is contained in:
@@ -36,7 +36,7 @@
|
||||
"STATUS-PLANNED": "Scheduled",
|
||||
"TIME_DEPARTURE": "Departure time",
|
||||
"TITLE": "Online Timetable",
|
||||
"YOU_SEARCH": "You searched",
|
||||
"YOU_SEARCH": "Previous searches",
|
||||
"LEG": "Leg",
|
||||
"TOTAL-FLYING-TIME": "Total flying time",
|
||||
"DETAILS-TITLE": "Flight details",
|
||||
@@ -316,7 +316,7 @@
|
||||
"BACK-SCHEDULE": "Back to Schedule",
|
||||
"BAGBELT": "Baggage Carousel Number",
|
||||
"BOARD_SCHEDULE": "Online Timetable and Schedule",
|
||||
"BUY-TICKET": "Buy",
|
||||
"BUY-TICKET": "Buy a ticket",
|
||||
"BUY-TICKET-HIT-RATE": "Buy a ticket at the Hit Fare",
|
||||
"CITY_CHANGE": "Different cities",
|
||||
"CITY_PLACEHOLDER": "Specify city",
|
||||
|
||||
@@ -36,7 +36,7 @@
|
||||
"STATUS-PLANNED": "Запланирован",
|
||||
"TIME_DEPARTURE": "Время отправления",
|
||||
"TITLE": "Онлайн-Табло",
|
||||
"YOU_SEARCH": "Вы искали",
|
||||
"YOU_SEARCH": "Ранее искали",
|
||||
"LEG": "Перелет",
|
||||
"TOTAL-FLYING-TIME": "Общее время в пути",
|
||||
"DETAILS-TITLE": "Детали рейса",
|
||||
@@ -316,7 +316,7 @@
|
||||
"BACK-SCHEDULE": "Вернуться к Расписанию",
|
||||
"BAGBELT": "Номер ленты выдачи багажа",
|
||||
"BOARD_SCHEDULE": "Онлайн-Табло и Расписание",
|
||||
"BUY-TICKET": "Купить",
|
||||
"BUY-TICKET": "Купить билет",
|
||||
"BUY-TICKET-HIT-RATE": "Купить билет по Хит тарифу",
|
||||
"CITY_CHANGE": "Смена города",
|
||||
"CITY_PLACEHOLDER": "Укажите город",
|
||||
|
||||
@@ -143,7 +143,10 @@ describe("useSearchHistory", () => {
|
||||
expect(result.current.items).toEqual([flightItem, routeItem]);
|
||||
});
|
||||
|
||||
it("limits history to 10 items", () => {
|
||||
// TIRREDESIGN-5 / TZ §4.1.14.3.8: flight-number history keeps at most 8
|
||||
// entries independently of board / schedule sections (one section
|
||||
// filling up must not evict entries from the others).
|
||||
it("caps flight-number section at 8 most-recent items", () => {
|
||||
const { result } = renderHook(() => useSearchHistory("ru"));
|
||||
|
||||
for (let i = 0; i < 12; i++) {
|
||||
@@ -156,9 +159,43 @@ describe("useSearchHistory", () => {
|
||||
});
|
||||
}
|
||||
|
||||
expect(result.current.items).toHaveLength(10);
|
||||
// Most recent should be first
|
||||
expect(result.current.items).toHaveLength(8);
|
||||
expect(result.current.items[0]?.url).toBe("/flight-11");
|
||||
expect(result.current.items[7]?.url).toBe("/flight-4");
|
||||
});
|
||||
|
||||
it("caps each section independently (board + schedule + flight-number)", () => {
|
||||
const { result } = renderHook(() => useSearchHistory("ru"));
|
||||
|
||||
for (let i = 0; i < 10; i++) {
|
||||
act(() => {
|
||||
result.current.add({
|
||||
type: "flight-number",
|
||||
url: `/flight-${i}`,
|
||||
label: `Flight ${i}`,
|
||||
});
|
||||
});
|
||||
act(() => {
|
||||
result.current.add({
|
||||
type: "board-route",
|
||||
url: `/departure-${i}`,
|
||||
label: `Departure ${i}`,
|
||||
});
|
||||
});
|
||||
act(() => {
|
||||
result.current.add({
|
||||
type: "schedule-route",
|
||||
url: `/schedule-${i}`,
|
||||
label: `Schedule ${i}`,
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
const byType = (t: string) =>
|
||||
result.current.items.filter((item) => item.type === t);
|
||||
expect(byType("flight-number")).toHaveLength(8);
|
||||
expect(byType("board-route")).toHaveLength(8);
|
||||
expect(byType("schedule-route")).toHaveLength(8);
|
||||
});
|
||||
|
||||
it("namespaces by language — different langs have separate history", () => {
|
||||
|
||||
@@ -106,7 +106,16 @@ const searchHistorySchema = z.array(searchHistoryItemSchema);
|
||||
// Storage key
|
||||
// ---------------------------------------------------------------------------
|
||||
|
||||
const MAX_HISTORY_ITEMS = 10;
|
||||
// TIRREDESIGN-5 / TZ §4.1.8: each section (board vs schedule vs flight-number)
|
||||
// retains up to 8 recent searches independently. A new board-route entry
|
||||
// cannot push a schedule-route entry out of the list.
|
||||
const MAX_PER_SECTION = 8;
|
||||
|
||||
function sectionFor(type: SearchHistoryItem["type"]): "board" | "schedule" | "flight-number" {
|
||||
if (type === "schedule-route") return "schedule";
|
||||
if (type === "flight-number") return "flight-number";
|
||||
return "board";
|
||||
}
|
||||
|
||||
function storageKey(lang: string): string {
|
||||
return `history_${lang}`;
|
||||
@@ -153,9 +162,23 @@ export function useSearchHistory(lang: string): UseSearchHistoryResult {
|
||||
return prev;
|
||||
}
|
||||
|
||||
// Remove any existing entry with same URL, then prepend
|
||||
// Remove any existing entry with the same URL, then prepend.
|
||||
const filtered = prev.filter((existing) => existing.url !== item.url);
|
||||
const next = [item, ...filtered].slice(0, MAX_HISTORY_ITEMS);
|
||||
const combined = [item, ...filtered];
|
||||
|
||||
// Cap per section — the board/schedule/flight-number buckets each
|
||||
// hold up to MAX_PER_SECTION (8) entries independently. This keeps
|
||||
// the resulting list ordered by recency across sections but
|
||||
// prevents a burst of board searches from evicting schedule
|
||||
// history (and vice versa).
|
||||
const perSectionCount = { board: 0, schedule: 0, "flight-number": 0 };
|
||||
const next: SearchHistoryItem[] = [];
|
||||
for (const entry of combined) {
|
||||
const bucket = sectionFor(entry.type);
|
||||
if (perSectionCount[bucket] >= MAX_PER_SECTION) continue;
|
||||
perSectionCount[bucket]++;
|
||||
next.push(entry);
|
||||
}
|
||||
|
||||
sessionStore.set(key, next, searchHistorySchema);
|
||||
if (typeof window !== "undefined") {
|
||||
|
||||
Reference in New Issue
Block a user