diff --git a/messages/de.json b/messages/de.json index 3c69a1f9a..0631b2cae 100644 --- a/messages/de.json +++ b/messages/de.json @@ -1522,6 +1522,7 @@ "feedbackThanks": "Danke für dein Feedback", "feedbackConfirmed": "Feedback gespeichert", "feedbackAlreadyRated": "Bereits bewertet", + "feedbackError": "Dein Feedback konnte nicht gespeichert werden. Bitte versuche es erneut.", "confidence": "Vertrauen", "confidenceAria": "Vertrauen: {value} von 100", "confidenceHigh": "Hohes Vertrauen", @@ -1543,6 +1544,7 @@ "regenerateAnalysis": "Analyse neu starten", "regenerateSuccess": "Analyse wurde neu erstellt", "warmAssessments": "Auswertungen vorbereiten", + "warmAssessmentsHint": "Auswertungen werden über Nacht automatisch aktualisiert. Bereite sie jetzt vor, um den neuesten Stand zu sehen.", "warmStarted": "Auswertungen werden im Hintergrund erstellt", "narrativeTitle": "Dein Zeitraum im Rückblick", "narrativeWeek": "Diese Woche", diff --git a/messages/en.json b/messages/en.json index 0004b2dcf..aaae6856f 100644 --- a/messages/en.json +++ b/messages/en.json @@ -1522,6 +1522,7 @@ "feedbackThanks": "Thanks for your feedback", "feedbackConfirmed": "Feedback recorded", "feedbackAlreadyRated": "Already rated", + "feedbackError": "Could not save your feedback. Please try again.", "confidence": "Confidence", "confidenceAria": "Confidence: {value} of 100", "confidenceHigh": "High confidence", @@ -1543,6 +1544,7 @@ "regenerateAnalysis": "Re-run analysis", "regenerateSuccess": "Analysis refreshed", "warmAssessments": "Prepare assessments", + "warmAssessmentsHint": "Assessments refresh overnight automatically. Prepare them now to read the latest.", "warmStarted": "Assessments are being prepared in the background", "narrativeTitle": "Your period in review", "narrativeWeek": "This week", diff --git a/messages/es.json b/messages/es.json index eb336d0f6..6ef303f74 100644 --- a/messages/es.json +++ b/messages/es.json @@ -1522,6 +1522,7 @@ "feedbackThanks": "Gracias por tu opinión", "feedbackConfirmed": "Opinión guardada", "feedbackAlreadyRated": "Ya valorado", + "feedbackError": "No se pudo guardar tu opinión. Inténtalo de nuevo.", "confidence": "Confianza", "confidenceAria": "Confianza: {value} de 100", "confidenceHigh": "Confianza alta", @@ -1543,6 +1544,7 @@ "regenerateAnalysis": "Volver a iniciar el análisis", "regenerateSuccess": "Análisis regenerado", "warmAssessments": "Preparar evaluaciones", + "warmAssessmentsHint": "Las evaluaciones se actualizan automáticamente durante la noche. Prepáralas ahora para ver lo más reciente.", "warmStarted": "Las evaluaciones se están preparando en segundo plano", "narrativeTitle": "Tu período en resumen", "narrativeWeek": "Esta semana", diff --git a/messages/fr.json b/messages/fr.json index 40ed28bfe..eee9b4a73 100644 --- a/messages/fr.json +++ b/messages/fr.json @@ -1522,6 +1522,7 @@ "feedbackThanks": "Merci pour ton retour", "feedbackConfirmed": "Retour enregistré", "feedbackAlreadyRated": "Déjà noté", + "feedbackError": "Impossible d’enregistrer ton retour. Réessaie.", "confidence": "Confiance", "confidenceAria": "Confiance : {value} sur 100", "confidenceHigh": "Confiance élevée", @@ -1543,6 +1544,7 @@ "regenerateAnalysis": "Relancer l’analyse", "regenerateSuccess": "Analyse regénérée", "warmAssessments": "Préparer les évaluations", + "warmAssessmentsHint": "Les évaluations sont actualisées automatiquement pendant la nuit. Préparez-les maintenant pour voir les dernières données.", "warmStarted": "Les évaluations sont en cours de préparation en arrière-plan", "narrativeTitle": "Votre période en revue", "narrativeWeek": "Cette semaine", diff --git a/messages/it.json b/messages/it.json index 921291c6a..9d71167f1 100644 --- a/messages/it.json +++ b/messages/it.json @@ -1522,6 +1522,7 @@ "feedbackThanks": "Grazie per il riscontro", "feedbackConfirmed": "Feedback salvato", "feedbackAlreadyRated": "Già valutato", + "feedbackError": "Impossibile salvare il tuo riscontro. Riprova.", "confidence": "Confidenza", "confidenceAria": "Confidenza: {value} su 100", "confidenceHigh": "Confidenza alta", @@ -1543,6 +1544,7 @@ "regenerateAnalysis": "Riavvia l’analisi", "regenerateSuccess": "Analisi rigenerata", "warmAssessments": "Prepara le valutazioni", + "warmAssessmentsHint": "Le valutazioni si aggiornano automaticamente durante la notte. Preparale ora per vedere i dati più recenti.", "warmStarted": "Le valutazioni vengono preparate in background", "narrativeTitle": "Il tuo periodo in sintesi", "narrativeWeek": "Questa settimana", diff --git a/messages/pl.json b/messages/pl.json index af00afbcc..bd93e4d39 100644 --- a/messages/pl.json +++ b/messages/pl.json @@ -1522,6 +1522,7 @@ "feedbackThanks": "Dzięki za opinię", "feedbackConfirmed": "Opinia zapisana", "feedbackAlreadyRated": "Już ocenione", + "feedbackError": "Nie udało się zapisać opinii. Spróbuj ponownie.", "confidence": "Pewność", "confidenceAria": "Pewność: {value} na 100", "confidenceHigh": "Wysoka pewność", @@ -1543,6 +1544,7 @@ "regenerateAnalysis": "Uruchom analizę ponownie", "regenerateSuccess": "Analiza wygenerowana ponownie", "warmAssessments": "Przygotuj analizy", + "warmAssessmentsHint": "Analizy odświeżają się automatycznie w nocy. Przygotuj je teraz, aby zobaczyć najnowsze dane.", "warmStarted": "Analizy są przygotowywane w tle", "narrativeTitle": "Twój okres w skrócie", "narrativeWeek": "Ten tydzień", diff --git a/src/app/insights/__tests__/page-skeletons.test.ts b/src/app/insights/__tests__/page-skeletons.test.ts index 017a434eb..1b225f1a2 100644 --- a/src/app/insights/__tests__/page-skeletons.test.ts +++ b/src/app/insights/__tests__/page-skeletons.test.ts @@ -4,51 +4,79 @@ import { join } from "node:path"; /** * v1.4.43 W11 — insights mother-page dynamic-skeleton guards. + * v1.11.3 — rewritten for the shared `BlockSkeleton` loader. * - * The three `next/dynamic` loading placeholders for - * `DailyBriefing` / `CorrelationRow` / `TrendsRow` used to reserve - * heights (`h-48` / `h-32` / `h-64`) noticeably shorter than the - * loaded content (~24 rem / ~20 rem each), which CLS-shifted the - * page on slow networks. They also lacked `motion-reduce:animate-none`, - * so motion-sensitive users saw a continuous pulse. + * The `next/dynamic` loading placeholders for the below-the-hero blocks + * used to be bespoke `
` snippets with hard-coded guessed + * heights. Those fixed heights pinned each placeholder taller or shorter + * than the resolved block, so the page CLS-shifted as each chunk landed. * - * This textual guard pins both the larger reserved heights and the - * motion-reduce class. The check is intentionally simple — render- - * mounting the page would haul in TanStack-Query / Auth / I18n - * scaffolding for a property a substring search already proves. + * E1 collapses all six loaders onto a single shared `BlockSkeleton` that + * routes through the `Skeleton` primitive (which carries + * `motion-reduce:animate-none`) and holds the row open with a `min-h` + * floor rather than a fixed `h-[Xrem]`. This guard pins the new contract: + * every loader uses `BlockSkeleton`, none re-introduces a hard-coded + * fixed height, and the decorative (un-mountable) cards stay `aria-hidden`. + * + * The check is intentionally textual — render-mounting the page would haul + * in TanStack-Query / Auth / I18n scaffolding for a property a substring + * search already proves. */ -describe("insights mother-page dynamic-skeleton heights + motion-reduce", () => { +describe("insights mother-page dynamic-skeleton loaders use the shared BlockSkeleton", () => { const src = readFileSync( join(process.cwd(), "src/app/insights/page.tsx"), "utf8", ); - it("DailyBriefing skeleton reserves h-[24rem]", () => { - expect(src).toMatch( - /DailyBriefing[\s\S]*?h-\[24rem\][^"]*motion-reduce:animate-none/, - ); + it("defines the shared BlockSkeleton helper", () => { + expect(src).toMatch(/function BlockSkeleton\(/); + // The helper renders the shared Skeleton primitive, which carries + // motion-reduce:animate-none, so every consumer inherits the guard. + expect(src).toMatch(/+ {t("insights.warmAssessmentsHint")} +