Skip to content
Open
Binary file added app/assets/codepecker-confused.webp
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
58 changes: 58 additions & 0 deletions app/components/shared/CpNotFound.vue
Comment thread
mirumodapon marked this conversation as resolved.
Original file line number Diff line number Diff line change
@@ -0,0 +1,58 @@
<script setup lang="ts">
const { t } = useI18n()
const localePath = useLocalePath()

function talkeMeHome() {
clearError({ redirect: localePath('/') })
}
</script>

<template>
<div class="px-4 py-12 flex flex-col min-h-[60vh] select-none items-center justify-center">
<div class="flex flex-col max-w-4xl w-full items-center justify-center lg:flex-row">
<div class="max-w-[200px] w-full -mb-10 lg:mb-0 lg:max-w-[300px] sm:max-w-[260px]">
<img
alt="codepecker-confused"
class="rounded-2xl h-auto w-full object-cover"
src="~/assets/codepecker-confused.webp"
>
</div>

<div class="text-center flex flex-col items-center lg:text-left lg:items-start">
<h1 class="text-5xl text-gray-500 leading-none tracking-tight font-bold sm:text-9xl">
404
</h1>

<p class="text-base text-gray-400 tracking-widest font-semibold mt-4 uppercase">
{{ t('pageNotFound') }}
</p>

<div class="my-5 bg-primary-300 h-[2px] w-12" />

<p class="text-sm text-gray-500 leading-relaxed max-w-md">
{{ t('description') }}
</p>

<div class="mt-6">
<button
class="text-sm text-primary-300 tracking-wide font-semibold px-8 py-2.5 border-2 border-primary-300 rounded-full inline-flex duration-200 items-center justify-center hover:text-white focus:outline-none hover:border-primary-400 hover:bg-primary-400"
@click="talkeMeHome"
>
{{ t('takeMeHome') }}
</button>
</div>
</div>
</div>
</div>
</template>

<i18n lang="yaml">
en:
pageNotFound: 'Page not found'
description: "Sorry, we could not find the page you were looking for."
takeMeHome: 'Take me home'
zh:
pageNotFound: '頁面不存在'
description: "抱歉我們找不到您要查找的頁面。"

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
description: "抱歉我們找不到您要查找的頁面。"
description: "抱歉,我們找不到您要找尋的頁面。"

感覺需要一點標點符號

takeMeHome: '帶我回首頁'
</i18n>
15 changes: 15 additions & 0 deletions app/error.vue
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
<script setup lang="ts">
import type { NuxtError } from '#app'
import CpNotFound from '~/components/shared/CpNotFound.vue'

defineProps({
error: Object as () => NuxtError,
})
</script>

<template>
<CpNotFound v-if="error?.status === 404" />
Comment thread
mirumodapon marked this conversation as resolved.
<p v-else>
{{ error?.status }} - {{ error?.statusText }}
</p>
</template>
11 changes: 7 additions & 4 deletions app/pages/[...slug].vue
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,13 @@ const slug = computed(() => {

const page = await useLocaleContent(slug, locale, defaultLocale)

if (!page.value) {
throw createError({
statusCode: 404,
statusMessage: 'Page not found',
})
Comment on lines +23 to +26

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge Make content 404s fatal on client navigation

When a user reaches a missing content slug via in-app navigation on this static Nuxt site, this throw createError() runs on the client; Nuxt treats client-side createError without fatal: true as non-fatal, so app/error.vue is not rendered and the old inline fallback has been removed. Mark this error fatal (or use showError) so both direct loads and client-side route changes show the 404 page.

Useful? React with 👍 / 👎.

}
Comment thread
pan93412 marked this conversation as resolved.

useSeoMeta({
title: () => page.value?.title,
description: () => page.value?.description,
Expand All @@ -35,8 +42,4 @@ useSeoMeta({
class="m-auto prose"
:value="page"
/>
<div v-else>
<h1>Page not found</h1>
<p>This page doesn't exist in {{ locale }} language.</p>
</div>
</template>
8 changes: 7 additions & 1 deletion app/pages/session/[id].vue
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,13 @@ const route = useRoute()
const router = useRouter()
const localePath = useLocalePath()

const { data } = await useFetch<SessionDetail>(`/api/session/${route.params.id}`)
const { data, error } = await useFetch<SessionDetail>(`/api/session/${route.params.id}`)

if (error.value) {
throw error.value.statusCode === 404
? createError({ status: 404, statusText: 'Page Not Found' })
: error.value
Comment on lines +13 to +15

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge Make session fetch failures reach error.vue

For an invalid or stale session URL reached through client-side navigation, this branch throws a non-fatal client error, so Nuxt does not render app/error.vue even though the direct SSR/static 404 path can work. Wrap the API error in a fatal Nuxt error before throwing so the session detail route consistently shows the 404/generic error page instead of leaving the user on the previous page with an unhandled navigation error.

Useful? React with 👍 / 👎.

}

const localeKey = computed(() => locale.value === 'zh' ? 'zh' : 'en')

Expand Down
Loading