Skip to content

Commit 093c226

Browse files
committed
feat: implement 404 Not Found page and integrate session not found handling
1 parent 45d7a10 commit 093c226

4 files changed

Lines changed: 84 additions & 20 deletions

File tree

120 KB
Loading
Lines changed: 49 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,49 @@
1+
<script setup lang="ts">
2+
const { t } = useI18n()
3+
</script>
4+
5+
<template>
6+
<div class="px-4 py-12 flex flex-col min-h-[60vh] select-none items-center justify-center">
7+
<div class="flex flex-col max-w-4xl w-full items-center justify-center lg:flex-row">
8+
<div class="max-w-[200px] w-full -mb-10 lg:mb-0 lg:max-w-[300px] sm:max-w-[260px]">
9+
<img
10+
alt="codepecker-confused"
11+
class="rounded-2xl h-auto w-full object-cover"
12+
src="~/assets/codepecker-confused.webp"
13+
>
14+
</div>
15+
16+
<div class="text-center flex flex-col items-center lg:text-left lg:items-start">
17+
<h1 class="text-5xl text-gray-700 leading-none tracking-tight font-bold sm:text-9xl">
18+
404
19+
</h1>
20+
21+
<p class="text-base text-gray-400 tracking-widest font-semibold mt-4 uppercase">
22+
PAGE NOT FOUND
23+
</p>
24+
25+
<div class="my-5 bg-primary-300 h-[2px] w-12" />
26+
27+
<p class="text-sm text-gray-500 leading-relaxed max-w-md">
28+
But if you don't change your direction, and if you keep looking, you may end up where you are heading.
29+
</p>
30+
31+
<div class="mt-6">
32+
<NuxtLink
33+
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"
34+
to="/"
35+
>
36+
{{ t('takeMeHome') }}
37+
</NuxtLink>
38+
</div>
39+
</div>
40+
</div>
41+
</div>
42+
</template>
43+
44+
<i18n lang="yaml">
45+
en:
46+
takeMeHome: 'Take me home'
47+
zh:
48+
takeMeHome: '返回首頁'
49+
</i18n>

app/pages/session.vue

Lines changed: 17 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -1,13 +1,12 @@
11
<script setup lang="ts">
22
import { prerenderRoutes } from 'nuxt/app'
3-
import { useI18n } from 'vue-i18n'
43
import CpSessionDaySelector from '~/components/feature/CpSessionDaySelector.vue'
54
import CpSessionList from '~/components/feature/CpSessionList.vue'
65
import CpSessionTable from '~/components/feature/CpSessionTable.vue'
76
8-
const { t } = useI18n()
9-
107
const { data } = await useFetch('/api/session')
8+
const route = useRoute()
9+
const isSessionNotFound = ref(false)
1110
1211
const manualSelectedDay = ref<string | null>(null)
1312
const days = computed(() => Object.keys(data?.value ?? {}).sort())
@@ -17,6 +16,16 @@ const selectedDay = computed({
1716
set: (value) => void (manualSelectedDay.value = value),
1817
})
1918
19+
provide('setSessionNotFound', (value: boolean) => {
20+
isSessionNotFound.value = value
21+
})
22+
23+
watch(() => route.params.id, (newId) => {
24+
if (!newId) {
25+
isSessionNotFound.value = false
26+
}
27+
})
28+
2029
prerenderRoutes(
2130
Object.values(data.value ?? {})
2231
.flat()
@@ -34,7 +43,10 @@ definePageMeta({
3443

3544
<ClientOnly>
3645
<template #fallback>
37-
<div class="flex flex-col-reverse sm:flex-col">
46+
<div
47+
v-if="!isSessionNotFound"
48+
class="flex flex-col-reverse sm:flex-col"
49+
>
3850
<!-- DaySelector -->
3951
<div class="px-6 pb-4 pt-3 flex w-screen justify-center">
4052
<div class="rounded-full bg-gray-200 h-12 w-1/2 animate-pulse" />
@@ -45,7 +57,7 @@ definePageMeta({
4557
</div>
4658
</template>
4759

48-
<template v-if="selectedDay">
60+
<template v-if="selectedDay && !isSessionNotFound">
4961
<div class="flex flex-col-reverse sm:flex-col">
5062
<CpSessionDaySelector
5163
v-model="selectedDay"
@@ -67,22 +79,7 @@ definePageMeta({
6779
:time-range="['09:00', '17:30']"
6880
/>
6981
</div>
70-
71-
<p v-if="!data?.[selectedDay]?.length">
72-
{{ t('noSession') }}
73-
</p>
7482
</template>
75-
76-
<p v-else>
77-
{{ t('noSession') }}
78-
</p>
7983
</ClientOnly>
8084
</main>
8185
</template>
82-
83-
<i18n lang="yaml">
84-
en:
85-
noSession: 'Not announced yet, stay tuned.'
86-
zh:
87-
noSession: '尚未公布,敬請期待。'
88-
</i18n>

app/pages/session/[id].vue

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
<script setup lang="ts">
22
import type { SessionDetail } from '#shared/types/session'
33
import CpSessionInfoCard from '~/components/feature/CpSessionInfoCard.vue'
4+
import CpNotFound from '~/components/shared/CpNotFound.vue'
45
56
const { locale } = useI18n()
67
const route = useRoute()
@@ -9,7 +10,10 @@ const localePath = useLocalePath()
910
1011
const { data } = await useFetch<SessionDetail>(`/api/session/${route.params.id}`)
1112
13+
const setSessionNotFound = inject<(value: boolean) => void>('setSessionNotFound')
14+
1215
const localeKey = computed(() => locale.value === 'zh' ? 'zh' : 'en')
16+
const hasData = computed(() => !!data.value)
1317
1418
const sessionInfo = computed(() => {
1519
if (!data.value) {
@@ -47,6 +51,12 @@ function onKeydown(e: KeyboardEvent) {
4751
}
4852
}
4953
54+
watchEffect(() => {
55+
if (setSessionNotFound) {
56+
setSessionNotFound(!hasData.value)
57+
}
58+
})
59+
5060
onMounted(() => {
5161
document.body.style.overflow = 'hidden'
5262
window.addEventListener('keydown', onKeydown)
@@ -60,6 +70,14 @@ onUnmounted(() => {
6070

6171
<template>
6272
<div
73+
v-if="!hasData"
74+
class="bg-white flex min-h-[70vh] w-[var(--viewport-width,100vw)] items-center justify-center"
75+
>
76+
<CpNotFound />
77+
</div>
78+
79+
<div
80+
v-else
6381
:aria-label="sessionInfo?.title"
6482
aria-modal="true"
6583
class="bg-black/50 inset-0 fixed z-50"

0 commit comments

Comments
 (0)