Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
299 changes: 154 additions & 145 deletions error.vue
Original file line number Diff line number Diff line change
@@ -1,160 +1,169 @@
<template>
<NuxtLayout>
<div>
<!-- Teleport target for Toggletip (rendered by SiteHeader). app.vue
provides it, but app.vue isn't mounted on the error page, so we must
provide it here too, before NuxtLayout mounts the header. -->
<div
v-if="error"
class="min-h-screen bg-white flex items-center justify-center px-8 md:px-4"
>
<div class="flex flex-col items-center text-center max-w-2xl w-full gap-6">
<div class="w-37 h-35 flex items-center justify-center md:w-25 md:h-24">
<img
v-if="statusCode === 404"
:src="error404Svg"
alt="Erreur 404"
class="w-full h-full object-contain"
>
<img
v-else-if="statusCode === 403"
:src="error403Svg"
alt="Erreur 403"
class="w-full h-full object-contain"
>
<img
v-else-if="statusCode === 410"
:src="error410Svg"
alt="Erreur 410"
class="w-full h-full object-contain"
>
<img
v-else-if="statusCode >= 500"
:src="error500Svg"
alt="Erreur serveur"
class="w-full h-full object-contain"
>
<img
v-else
:src="error500Svg"
alt="Erreur"
class="w-full h-full object-contain"
>
</div>

<h1 class="text-6xl font-extrabold leading-none text-gray-title m-0 md:text-5xl">
<template v-if="statusCode">
{{ statusCode }}
</template>
<template v-else>
{{ $t('Erreur') }}
</template>
</h1>

<h2 class="text-2xl font-extrabold leading-9 text-gray-title m-0 md:text-xl md:leading-8">
<template v-if="statusCode === 404">
{{ $t('Page non trouvée') }}
</template>
<template v-else-if="statusCode === 403">
{{ $t('Accès interdit') }}
</template>
<template v-else-if="statusCode === 410">
{{ $t('Contenu supprimé') }}
</template>
<template v-else-if="statusCode >= 500">
{{ $t('Erreur interne du serveur') }}
</template>
<template v-else>
{{ $t('Erreur inattendue') }}
</template>
</h2>

<p class="text-base font-normal leading-6 text-gray-title m-0 text-center md:text-sm md:leading-5">
<template v-if="statusCode === 404">
{{ $t("Désolé, nous n'avons pas trouvé la page que vous recherchez. Il se peut que le lien soit incorrect ou que la page ait été déplacée ou supprimée. Si cela vous semble une erreur n'hésitez pas à") }}
<NuxtLink
to="/support"
class="text-datagouv-dark"
id="tooltips"
class="datagouv-components h-0"
/>
<NuxtLayout>
<div
v-if="error"
class="min-h-screen bg-white flex items-center justify-center px-8 md:px-4"
>
<div class="flex flex-col items-center text-center max-w-2xl w-full gap-6">
<div class="w-37 h-35 flex items-center justify-center md:w-25 md:h-24">
<img
v-if="statusCode === 404"
:src="error404Svg"
alt="Erreur 404"
class="w-full h-full object-contain"
>
{{ $t('nous écrire') }}
</NuxtLink>.
</template>
<template v-else-if="statusCode === 403">
{{ $t("Vous n'avez pas la permission d'accéder à cette page. Cela peut être dû à des restrictions de droits ou à une authentification manquante. Si cela vous semble une erreur n'hésitez pas à") }}
<NuxtLink
to="/support"
class="text-datagouv-dark"
<img
v-else-if="statusCode === 403"
:src="error403Svg"
alt="Erreur 403"
class="w-full h-full object-contain"
>
{{ $t('nous écrire') }}
</NuxtLink>.
</template>
<template v-else-if="statusCode === 410">
{{ $t("Ce contenu a été volontairement retiré. Il n'est plus accessible, car il a été supprimé définitivement par son producteur. Si cela vous semble une erreur n'hésitez pas à") }}
<NuxtLink
to="/support"
class="text-datagouv-dark"
<img
v-else-if="statusCode === 410"
:src="error410Svg"
alt="Erreur 410"
class="w-full h-full object-contain"
>
{{ $t('nous écrire') }}
</NuxtLink>.
</template>
<template v-else-if="statusCode >= 500">
{{ $t("Une erreur interne est survenue. Veuillez nous excuser pour la gêne occasionnée. Si le problème persiste, merci de nous écrire avec les détails de l'erreur pour que nous puissions y remédier rapidement.") }}
</template>
<template v-else>
{{ $t("Une erreur inattendue s'est produite. Veuillez nous excuser pour la gêne occasionnée. Si le problème persiste, merci de") }}
<NuxtLink
to="/support"
class="text-datagouv-dark"
<img
v-else-if="statusCode >= 500"
:src="error500Svg"
alt="Erreur serveur"
class="w-full h-full object-contain"
>
{{ $t('nous écrire') }}
</NuxtLink>
{{ $t("avec les détails de l'erreur pour que nous puissions y remédier rapidement.") }}
<span
v-if="errorMessage"
class="fr-mt-2w fr-text--sm fr-text--bold"
>{{ errorMessage }}</span>
</template>
</p>

<div
v-if="statusCode >= 500 && errorMessage"
class="fr-mt-4w fr-p-2w"
>
<p class="fr-text--sm">
<span class="fr-text--bold">{{ $t('Code erreur') }} :&nbsp;</span>
<span class="font-mono">{{ errorMessage }}</span>
<CopyButton
:label="$t('Copier le code erreur')"
:copied-label="$t('Code erreur copié !')"
:text="errorMessage || ''"
class="ml-2"
/>
<pre
v-if="error.stack"
class="text-left"
<img
v-else
:src="error500Svg"
alt="Erreur"
class="w-full h-full object-contain"
>
{{ error.stack }}
</pre>
</div>

<h1 class="text-6xl font-extrabold leading-none text-gray-title m-0 md:text-5xl">
<template v-if="statusCode">
{{ statusCode }}
</template>
<template v-else>
{{ $t('Erreur') }}
</template>
</h1>

<h2 class="text-2xl font-extrabold leading-9 text-gray-title m-0 md:text-xl md:leading-8">
<template v-if="statusCode === 404">
{{ $t('Page non trouvée') }}
</template>
<template v-else-if="statusCode === 403">
{{ $t('Accès interdit') }}
</template>
<template v-else-if="statusCode === 410">
{{ $t('Contenu supprimé') }}
</template>
<template v-else-if="statusCode >= 500">
{{ $t('Erreur interne du serveur') }}
</template>
<template v-else>
{{ $t('Erreur inattendue') }}
</template>
</h2>

<p class="text-base font-normal leading-6 text-gray-title m-0 text-center md:text-sm md:leading-5">
<template v-if="statusCode === 404">
{{ $t("Désolé, nous n'avons pas trouvé la page que vous recherchez. Il se peut que le lien soit incorrect ou que la page ait été déplacée ou supprimée. Si cela vous semble une erreur n'hésitez pas à") }}
<NuxtLink
to="/support"
class="text-datagouv-dark"
>
{{ $t('nous écrire') }}
</NuxtLink>.
</template>
<template v-else-if="statusCode === 403">
{{ $t("Vous n'avez pas la permission d'accéder à cette page. Cela peut être dû à des restrictions de droits ou à une authentification manquante. Si cela vous semble une erreur n'hésitez pas à") }}
<NuxtLink
to="/support"
class="text-datagouv-dark"
>
{{ $t('nous écrire') }}
</NuxtLink>.
</template>
<template v-else-if="statusCode === 410">
{{ $t("Ce contenu a été volontairement retiré. Il n'est plus accessible, car il a été supprimé définitivement par son producteur. Si cela vous semble une erreur n'hésitez pas à") }}
<NuxtLink
to="/support"
class="text-datagouv-dark"
>
{{ $t('nous écrire') }}
</NuxtLink>.
</template>
<template v-else-if="statusCode >= 500">
{{ $t("Une erreur interne est survenue. Veuillez nous excuser pour la gêne occasionnée. Si le problème persiste, merci de nous écrire avec les détails de l'erreur pour que nous puissions y remédier rapidement.") }}
</template>
<template v-else>
{{ $t("Une erreur inattendue s'est produite. Veuillez nous excuser pour la gêne occasionnée. Si le problème persiste, merci de") }}
<NuxtLink
to="/support"
class="text-datagouv-dark"
>
{{ $t('nous écrire') }}
</NuxtLink>
{{ $t("avec les détails de l'erreur pour que nous puissions y remédier rapidement.") }}
<span
v-if="errorMessage"
class="fr-mt-2w fr-text--sm fr-text--bold"
>{{ errorMessage }}</span>
</template>
</p>
</div>

<div class="mt-0">
<BrandedButton
v-if="statusCode >= 500"
href="/support"
color="primary"
size="lg"
>
{{ $t("Nous écrire") }}
</BrandedButton>
<BrandedButton
v-else
href="/"
color="primary"
size="lg"
<div
v-if="statusCode >= 500 && errorMessage"
class="fr-mt-4w fr-p-2w"
>
{{ $t("Retour à l'accueil") }}
</BrandedButton>
<p class="fr-text--sm">
<span class="fr-text--bold">{{ $t('Code erreur') }} :&nbsp;</span>
<span class="font-mono">{{ errorMessage }}</span>
<CopyButton
:label="$t('Copier le code erreur')"
:copied-label="$t('Code erreur copié !')"
:text="errorMessage || ''"
class="ml-2"
/>
<pre
v-if="error.stack"
class="text-left"
>
{{ error.stack }}
</pre>
</p>
</div>

<div class="mt-0">
<BrandedButton
v-if="statusCode >= 500"
href="/support"
color="primary"
size="lg"
>
{{ $t("Nous écrire") }}
</BrandedButton>
<BrandedButton
v-else
href="/"
color="primary"
size="lg"
>
{{ $t("Retour à l'accueil") }}
</BrandedButton>
</div>
</div>
</div>
</div>
</NuxtLayout>
</NuxtLayout>
</div>
</template>

<script setup lang="ts">
Expand Down
23 changes: 23 additions & 0 deletions middleware/clear-error.global.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
export default defineNuxtRouteMiddleware((to, from) => {
// When error.vue is displayed, Nuxt keeps it mounted until the error state is
// cleared (it is NOT cleared automatically on navigation, see nuxt#22683).
// Following an in-app link then crashes/blanks the page because the error tree
// is still mounted.
// We must NOT clear synchronously here: doing so re-renders the *current*
// (errored) route before the navigation commits, which re-mounts the page and
// re-runs its failing useFetch (see nuxt#27410). Instead, defer the clear until
// the navigation has committed to `to`, so the error tree is gone and the
// target page renders cleanly.
// Client-only: the error page is rendered fresh on each SSR request. The
// `to !== from` guard, plus only registering when an error is already present,
// keeps the error page rendered on initial load and reload.
if (import.meta.server) return
const error = useError()
if (!error.value || to.fullPath === from.fullPath) return

const router = useRouter()
const stop = router.afterEach(() => {
stop()
clearError()
})
})
28 changes: 15 additions & 13 deletions pages/pages/[...slug].vue
Original file line number Diff line number Diff line change
Expand Up @@ -16,10 +16,7 @@
:status
:data="data"
>
<div
v-if="status === 'success' && data"
:class="markdownClasses"
>
<div :class="markdownClasses">
<MarkdownViewer
v-if="data.extension === 'md'"
:content="data.content"
Expand All @@ -30,13 +27,6 @@
v-else
/>
</div>
<div
v-else
class="py-9 prose"
>
<h1>{{ $t('Erreur 404') }}</h1>
<p>{{ $t("La page que vous recherchez est introuvable.") }}</p>
</div>
</LoadingBlock>
</div>
</template>
Expand All @@ -49,14 +39,26 @@ import BreadcrumbItem from '~/components/Breadcrumbs/BreadcrumbItem.vue'
const route = useRoute()
const siteConfig = useSiteConfig()

const { data, status } = useFetch<{
// An empty slug (/pages) is not a valid page: return a clean 404 right away.
// Fetching it would build the URL `/nuxt-api/pages/` whose trailing slash gets
// 308-redirected, which ofetch follows without raising an error.
const slug = route.params.slug ? (route.params.slug as string[]).join('/') : ''
if (!slug) {
throw createError({ statusCode: 404, statusMessage: 'Page Not Found', fatal: true })
}

const { data, status, error } = await useFetch<{
data: {
title: string
description?: string
}
extension: string
content: string
}>(`/nuxt-api/pages/${route.params.slug ? (route.params.slug as string[]).join('/') : ''}`)
}>(`/nuxt-api/pages/${slug}`)

if (error.value || !data.value) {
throw createError({ statusCode: 404, statusMessage: 'Page Not Found', fatal: true })
}

const title = computed(() => data.value?.data.title)
const description = computed(() => data.value?.data.description)
Expand Down
Loading
Loading