fix(journey): add localized error messages for dismissing suggestions

This commit is contained in:
Luffy
2026-10-16 15:29:41 +08:00
committed by Maurice
parent 5622113348
commit d229613e0e
27 changed files with 34 additions and 8 deletions
+8 -5
View File
@@ -12,6 +12,8 @@ import { useAddonStore } from '../store/addonStore';
import { usePermissionsStore } from '../store/permissionsStore';
import JourneyPage from './JourneyPage';
const { mockToastError } = vi.hoisted(() => ({ mockToastError: vi.fn() }));
// ── Mocks ────────────────────────────────────────────────────────────────────
const mockNavigate = vi.fn();
@@ -24,6 +26,10 @@ vi.mock('../components/Layout/Navbar', () => ({
default: () => <nav data-testid="navbar" />,
}));
vi.mock('../components/shared/Toast', () => ({
useToast: () => ({ error: mockToastError }),
}));
// ── Helpers ──────────────────────────────────────────────────────────────────
let _seq = 500;
@@ -79,7 +85,6 @@ function setupDefaultHandlers(journeys: ReturnType<typeof buildJourneyListItem>[
beforeEach(() => {
vi.clearAllMocks();
window.localStorage.clear();
delete window.__addToast;
mockNavigate.mockReset();
resetAllStores();
seedDefaults();
@@ -223,8 +228,7 @@ describe('JourneyPage', () => {
it('FE-PAGE-JOURNEY-007d: keeps the suggestion visible and reports a storage failure', async () => {
const user = userEvent.setup();
const suggestion = { id: 992, title: 'Coast Trip', start_date: '2026-03-01', end_date: '2026-03-02', place_count: 2 };
const addToast = vi.fn();
window.__addToast = addToast;
mockToastError.mockClear();
const setItem = vi.spyOn(Storage.prototype, 'setItem').mockImplementation(() => {
throw new DOMException('Storage is full', 'QuotaExceededError');
});
@@ -236,10 +240,9 @@ describe('JourneyPage', () => {
render(<JourneyPage />);
await user.click(await screen.findByRole('button', { name: 'Dismiss' }));
expect(addToast).toHaveBeenCalledWith('Could not dismiss this suggestion', 'error', undefined);
expect(mockToastError).toHaveBeenCalledWith('Could not dismiss this suggestion');
expect(screen.getByText(/Trip just ended/i)).toBeInTheDocument();
setItem.mockRestore();
delete window.__addToast;
});
it('FE-PAGE-JOURNEY-007e: deletes dismissed IDs absent from the suggestions response', async () => {
+1 -3
View File
@@ -78,8 +78,6 @@ export function useJourney() {
useEffect(() => {
let cancelled = false
setSuggestions([])
setDismissedSuggestions(readDismissedSuggestions(userId))
journeyApi.suggestions().then(d => {
if (cancelled) return
const trips = d.trips || []
@@ -92,7 +90,7 @@ export function useJourney() {
try {
saveDismissedSuggestions(userId, retained)
} catch {
toast.error(t('journey.frontpage.dismissError'))
// Pruning is best effort; a failed write leaves the old IDs to retry later.
}
}
}).catch(() => {})
+1
View File
@@ -141,6 +141,7 @@ const journey: TranslationStrings = {
'journey.frontpage.suggestionLabel': 'Trip just ended', // en-fallback
'journey.frontpage.suggestionText': 'Turn <strong>{title}</strong> into a Journey', // en-fallback
'journey.frontpage.dismiss': 'Dismiss', // en-fallback
'journey.frontpage.dismissError': 'تعذر تجاهل هذا الاقتراح',
'journey.frontpage.journeyName': 'Journey Name', // en-fallback
'journey.frontpage.namePlaceholder': 'e.g. Southeast Asia 2026', // en-fallback
'journey.frontpage.selectTrips': 'Select Trips', // en-fallback
+1
View File
@@ -78,6 +78,7 @@ const journey: TranslationStrings = {
'journey.frontpage.suggestionLabel': 'Səyahət indicə başa çatdı',
'journey.frontpage.suggestionText': '<strong>{title}</strong> səyahətini gündəliyə çevirin',
'journey.frontpage.dismiss': 'Bağla',
'journey.frontpage.dismissError': 'Bu təklifi gizlətmək mümkün olmadı',
'journey.frontpage.journeyName': 'Səyahət gündəliyinin adı',
'journey.frontpage.namePlaceholder': 'məs. Cənub-Şərqi Asiya 2026',
'journey.frontpage.selectTrips': 'Səyahətləri seç',
+1
View File
@@ -70,6 +70,7 @@ const journey: TranslationStrings = {
'journey.frontpage.suggestionLabel': 'A viagem acabou de terminar',
'journey.frontpage.suggestionText': 'Transforme <strong>{title}</strong> em uma jornada',
'journey.frontpage.dismiss': 'Dispensar',
'journey.frontpage.dismissError': 'Não foi possível dispensar esta sugestão',
'journey.frontpage.journeyName': 'Nome da jornada',
'journey.frontpage.namePlaceholder': 'ex. Sudeste Asiático 2026',
'journey.frontpage.selectTrips': 'Selecionar viagens',
+1
View File
@@ -70,6 +70,7 @@ const journey: TranslationStrings = {
'journey.frontpage.suggestionLabel': 'El viatge acaba de terminar',
'journey.frontpage.suggestionText': 'Converteix <strong>{title}</strong> en una travesia',
'journey.frontpage.dismiss': 'Descarta',
'journey.frontpage.dismissError': "No s'ha pogut descartar aquest suggeriment",
'journey.frontpage.journeyName': 'Nom de la travesia',
'journey.frontpage.namePlaceholder': 'p. ex. Sud-est Asiàtic 2026',
'journey.frontpage.selectTrips': 'Selecciona viatges',
+1
View File
@@ -71,6 +71,7 @@ const journey: TranslationStrings = {
'journey.frontpage.suggestionLabel': 'Cesta právě skončila',
'journey.frontpage.suggestionText': 'Proměňte <strong>{title}</strong> v cestovní deník',
'journey.frontpage.dismiss': 'Zavřít',
'journey.frontpage.dismissError': 'Tento návrh se nepodařilo skrýt',
'journey.frontpage.journeyName': 'Název cestovního deníku',
'journey.frontpage.namePlaceholder': 'např. Jihovýchodní Asie 2026',
'journey.frontpage.selectTrips': 'Vybrat cesty',
+1
View File
@@ -70,6 +70,7 @@ const journey: TranslationStrings = {
'journey.frontpage.suggestionLabel': 'Trip gerade beendet',
'journey.frontpage.suggestionText': 'Verwandle <strong>{title}</strong> in eine Journey',
'journey.frontpage.dismiss': 'Schließen',
'journey.frontpage.dismissError': 'Dieser Vorschlag konnte nicht ausgeblendet werden',
'journey.frontpage.journeyName': 'Journey-Name',
'journey.frontpage.namePlaceholder': 'z.B. Südostasien 2026',
'journey.frontpage.selectTrips': 'Trips auswählen',
+1
View File
@@ -70,6 +70,7 @@ const journey: TranslationStrings = {
'journey.frontpage.suggestionLabel': 'El viaje acaba de terminar',
'journey.frontpage.suggestionText': 'Convierte <strong>{title}</strong> en una travesía',
'journey.frontpage.dismiss': 'Descartar',
'journey.frontpage.dismissError': 'No se pudo descartar esta sugerencia',
'journey.frontpage.journeyName': 'Nombre de la travesía',
'journey.frontpage.namePlaceholder': 'p. ej. Sudeste Asiático 2026',
'journey.frontpage.selectTrips': 'Seleccionar viajes',
+1
View File
@@ -70,6 +70,7 @@ const journey: TranslationStrings = {
'journey.frontpage.suggestionLabel': 'Reis just lõppes',
'journey.frontpage.suggestionText': 'Tee reisist <strong>{title}</strong> reisilugu',
'journey.frontpage.dismiss': 'Jäta kõrvale',
'journey.frontpage.dismissError': 'Soovitust ei saanud peita',
'journey.frontpage.journeyName': 'Reisiloo nimi',
'journey.frontpage.namePlaceholder': 'Nt Kagu-Aasia 2026',
'journey.frontpage.selectTrips': 'Vali reisid',
+1
View File
@@ -70,6 +70,7 @@ const journey: TranslationStrings = {
'journey.frontpage.suggestionLabel': 'Voyage terminé récemment',
'journey.frontpage.suggestionText': 'Transformez <strong>{title}</strong> en journal de voyage',
'journey.frontpage.dismiss': 'Ignorer',
'journey.frontpage.dismissError': 'Impossible d’ignorer cette suggestion',
'journey.frontpage.journeyName': 'Nom du journal',
'journey.frontpage.namePlaceholder': 'ex. Asie du Sud-Est 2026',
'journey.frontpage.selectTrips': 'Sélectionner des voyages',
+1
View File
@@ -70,6 +70,7 @@ const journey: TranslationStrings = {
'journey.frontpage.suggestionLabel': 'Το ταξίδι μόλις τελείωσε',
'journey.frontpage.suggestionText': 'Μετατρέψτε το <strong>{title}</strong> σε Ταξίδι',
'journey.frontpage.dismiss': 'Απόρριψη',
'journey.frontpage.dismissError': 'Δεν ήταν δυνατή η απόκρυψη αυτής της πρότασης',
'journey.frontpage.journeyName': 'Όνομα Ταξιδιού',
'journey.frontpage.namePlaceholder': 'π.χ. Νοτιοανατολική Ασία 2026',
'journey.frontpage.selectTrips': 'Επιλογή Ταξιδιών',
+1
View File
@@ -70,6 +70,7 @@ const journey: TranslationStrings = {
'journey.frontpage.suggestionLabel': 'Az út épp véget ért',
'journey.frontpage.suggestionText': 'Alakítsd a(z) <strong>{title}</strong> útinaplóvá',
'journey.frontpage.dismiss': 'Elvetés',
'journey.frontpage.dismissError': 'Nem sikerült elrejteni ezt a javaslatot',
'journey.frontpage.journeyName': 'Útinapló neve',
'journey.frontpage.namePlaceholder': 'pl. Délkelet-Ázsia 2026',
'journey.frontpage.selectTrips': 'Utak kiválasztása',
+1
View File
@@ -69,6 +69,7 @@ const journey: TranslationStrings = {
'journey.frontpage.suggestionLabel': 'Perjalanan baru saja selesai',
'journey.frontpage.suggestionText': 'Ubah <strong>{title}</strong> menjadi Journey',
'journey.frontpage.dismiss': 'Tutup',
'journey.frontpage.dismissError': 'Saran ini tidak dapat disembunyikan',
'journey.frontpage.journeyName': 'Nama Journey',
'journey.frontpage.namePlaceholder': 'mis. Asia Tenggara 2026',
'journey.frontpage.selectTrips': 'Pilih Perjalanan',
+1
View File
@@ -70,6 +70,7 @@ const journey: TranslationStrings = {
'journey.frontpage.suggestionLabel': 'Viaggio appena terminato',
'journey.frontpage.suggestionText': 'Trasforma <strong>{title}</strong> in un diario di viaggio',
'journey.frontpage.dismiss': 'Ignora',
'journey.frontpage.dismissError': 'Impossibile ignorare questo suggerimento',
'journey.frontpage.journeyName': 'Nome del diario',
'journey.frontpage.namePlaceholder': 'es. Sud-est asiatico 2026',
'journey.frontpage.selectTrips': 'Seleziona viaggi',
+1
View File
@@ -69,6 +69,7 @@ const journey: TranslationStrings = {
'journey.frontpage.suggestionLabel': '旅行が終了しました',
'journey.frontpage.suggestionText': '<strong>{title}</strong>を日記にしよう',
'journey.frontpage.dismiss': '閉じる',
'journey.frontpage.dismissError': 'この提案を非表示にできませんでした',
'journey.frontpage.journeyName': '日記名',
'journey.frontpage.namePlaceholder': '例:東南アジア 2026',
'journey.frontpage.selectTrips': '旅行を選択',
+1
View File
@@ -69,6 +69,7 @@ const journey: TranslationStrings = {
'journey.frontpage.suggestionLabel': '여행이 방금 종료됨',
'journey.frontpage.suggestionText': '<strong>{title}</strong>을(를) Journey로 만들어보세요',
'journey.frontpage.dismiss': '닫기',
'journey.frontpage.dismissError': '이 제안을 숨길 수 없습니다',
'journey.frontpage.journeyName': 'Journey 이름',
'journey.frontpage.namePlaceholder': '예: 동남아시아 2026',
'journey.frontpage.selectTrips': '여행 선택',
+1
View File
@@ -70,6 +70,7 @@ const journey: TranslationStrings = {
'journey.frontpage.suggestionLabel': 'Reis net afgelopen',
'journey.frontpage.suggestionText': 'Maak van <strong>{title}</strong> een reisverslag',
'journey.frontpage.dismiss': 'Sluiten',
'journey.frontpage.dismissError': 'Deze suggestie kon niet worden verborgen',
'journey.frontpage.journeyName': 'Naam reisverslag',
'journey.frontpage.namePlaceholder': 'bijv. Zuidoost-Azië 2026',
'journey.frontpage.selectTrips': 'Selecteer reizen',
+1
View File
@@ -72,6 +72,7 @@ const journey: TranslationStrings = {
'journey.frontpage.suggestionLabel': 'Podróż właśnie się zakończyła',
'journey.frontpage.suggestionText': 'Zamień <strong>{title}</strong> w dziennik podróży',
'journey.frontpage.dismiss': 'Odrzuć',
'journey.frontpage.dismissError': 'Nie udało się ukryć tej sugestii',
'journey.frontpage.journeyName': 'Nazwa dziennika podróży',
'journey.frontpage.namePlaceholder': 'np. Azja Południowo-Wschodnia 2026',
'journey.frontpage.selectTrips': 'Wybierz podróże',
+1
View File
@@ -72,6 +72,7 @@ const journey: TranslationStrings = {
'journey.frontpage.suggestionLabel': 'Поездка только что завершилась',
'journey.frontpage.suggestionText': 'Превратите <strong>{title}</strong> в путешествие',
'journey.frontpage.dismiss': 'Скрыть',
'journey.frontpage.dismissError': 'Не удалось скрыть это предложение',
'journey.frontpage.journeyName': 'Название путешествия',
'journey.frontpage.namePlaceholder': 'напр. Юго-Восточная Азия 2026',
'journey.frontpage.selectTrips': 'Выбрать поездки',
+1
View File
@@ -71,6 +71,7 @@ const journey: TranslationStrings = {
'journey.frontpage.suggestionLabel': 'Cesta práve skončila',
'journey.frontpage.suggestionText': 'Premeňte <strong>{title}</strong> na cestovný denník',
'journey.frontpage.dismiss': 'Zavrieť',
'journey.frontpage.dismissError': 'Tento návrh sa nepodarilo skryť',
'journey.frontpage.journeyName': 'Názov cestovného denníka',
'journey.frontpage.namePlaceholder': 'napr. Juhovýchodná Ázia 2026',
'journey.frontpage.selectTrips': 'Vybrať cesty',
+1
View File
@@ -70,6 +70,7 @@ const journey: TranslationStrings = {
'journey.frontpage.suggestionLabel': 'Resan har just avslutats',
'journey.frontpage.suggestionText': 'Förvandla <strong>{title}</strong> till en Journey',
'journey.frontpage.dismiss': 'Stäng',
'journey.frontpage.dismissError': 'Det gick inte att dölja förslaget',
'journey.frontpage.journeyName': 'Journey namn',
'journey.frontpage.namePlaceholder': 't.ex. Sydostasien 2026',
'journey.frontpage.selectTrips': 'Välj resor',
+1
View File
@@ -69,6 +69,7 @@ const journey: TranslationStrings = {
'journey.frontpage.suggestionLabel': 'การเดินทางเพิ่งสิ้นสุด',
'journey.frontpage.suggestionText': 'เปลี่ยน <strong>{title}</strong> ให้เป็นการเดินทาง',
'journey.frontpage.dismiss': 'ยกเลิก',
'journey.frontpage.dismissError': 'ไม่สามารถซ่อนคำแนะนำนี้ได้',
'journey.frontpage.journeyName': 'ชื่อการเดินทาง',
'journey.frontpage.namePlaceholder': 'เช่น เอเชียตะวันออกเฉียงใต้ 2026',
'journey.frontpage.selectTrips': 'เลือกการเดินทาง',
+1
View File
@@ -70,6 +70,7 @@ const journey: TranslationStrings = {
'journey.frontpage.suggestionLabel': 'Seyahat yeni bitti',
'journey.frontpage.suggestionText': "<strong>{title}</strong> seyahatini Journey'e dönüştür",
'journey.frontpage.dismiss': 'Kapat',
'journey.frontpage.dismissError': 'Bu öneri gizlenemedi',
'journey.frontpage.journeyName': 'Journey Adı',
'journey.frontpage.namePlaceholder': 'örn. Güneydoğu Asya 2026',
'journey.frontpage.selectTrips': 'Seyahat Seç',
+1
View File
@@ -72,6 +72,7 @@ const journey: TranslationStrings = {
'journey.frontpage.suggestionLabel': 'Поїздка щойно завершилась',
'journey.frontpage.suggestionText': 'Перетворіть <strong>{title}</strong> на подорож',
'journey.frontpage.dismiss': 'Приховати',
'journey.frontpage.dismissError': 'Не вдалося приховати цю пропозицію',
'journey.frontpage.journeyName': 'Назва подорожі',
'journey.frontpage.namePlaceholder': 'наприклад, Південно-Східна Азія 2026',
'journey.frontpage.selectTrips': 'Оберіть поїздки',
+1
View File
@@ -69,6 +69,7 @@ const journey: TranslationStrings = {
'journey.frontpage.suggestionLabel': 'Chuyến đi vừa kết thúc',
'journey.frontpage.suggestionText': 'Biến <strong>{title}</strong> thành một cuộc hành trình',
'journey.frontpage.dismiss': 'Miễn nhiệm',
'journey.frontpage.dismissError': 'Không thể ẩn gợi ý này',
'journey.frontpage.journeyName': 'Tên hành trình',
'journey.frontpage.namePlaceholder': 'ví dụ. Đông Nam Á 2026',
'journey.frontpage.selectTrips': 'Chọn chuyến đi',
+1
View File
@@ -69,6 +69,7 @@ const journey: TranslationStrings = {
'journey.frontpage.suggestionLabel': '旅行剛結束',
'journey.frontpage.suggestionText': '將 <strong>{title}</strong> 變成一段旅程',
'journey.frontpage.dismiss': '忽略',
'journey.frontpage.dismissError': '無法忽略此建議',
'journey.frontpage.journeyName': '旅程名稱',
'journey.frontpage.namePlaceholder': '例如 東南亞 2026',
'journey.frontpage.selectTrips': '選擇旅行',