feat: Implement error and not found pages with improved user feedback and navigation
This commit is contained in:
@@ -1,29 +1,41 @@
|
||||
import Link from "next/link";
|
||||
import { Globe2, Mail, MapPin, Phone } from "lucide-react";
|
||||
import { HeritageSeal } from "@/app/components/landing/HeritageOrnament";
|
||||
import { HeritageSeal, SectionDivider } from "@/app/components/landing/HeritageOrnament";
|
||||
import { contact, navigation } from "@/app/content";
|
||||
|
||||
export function SiteFooter() {
|
||||
return (
|
||||
<footer id="contact" className="px-4 pb-8 sm:px-6">
|
||||
<div className="mx-auto grid max-w-7xl gap-8 border-t border-[var(--surface-border)] pt-8 lg:grid-cols-[1.1fr_0.72fr_0.82fr]">
|
||||
<footer id="contact" className="px-4 pb-10 sm:px-6">
|
||||
<SectionDivider className="mx-auto max-w-7xl pb-12 pt-2" />
|
||||
|
||||
<div className="mx-auto grid max-w-7xl gap-8 lg:grid-cols-[1.15fr_0.7fr_0.85fr]">
|
||||
<div className="min-w-0">
|
||||
<p className="public-display text-xl font-bold text-[var(--tour-ink)]">VR360 Như Quỳnh</p>
|
||||
<p className="mt-3 max-w-[62ch] text-sm leading-6 text-[var(--muted-foreground)]">
|
||||
Cổng số hóa di tích và không gian văn hóa Xã Như Quỳnh, tỉnh Hưng Yên bằng công nghệ VR360.
|
||||
<div className="flex items-center gap-3">
|
||||
<span className="grid h-11 w-11 shrink-0 place-items-center rounded-full border border-[rgb(142_95_11_/_0.24)] bg-[linear-gradient(135deg,rgb(255_255_255_/_0.94),rgb(232_219_176_/_0.74))] shadow-[inset_0_1px_0_rgb(255_255_255_/_0.92)]">
|
||||
<HeritageSeal className="h-6 w-6 text-[var(--primary)]" />
|
||||
</span>
|
||||
<p className="public-display text-xl font-bold leading-tight text-[var(--tour-ink)]">VR360 Như Quỳnh</p>
|
||||
</div>
|
||||
<p className="mt-4 max-w-[58ch] text-sm leading-7 text-[var(--muted-foreground)]">
|
||||
Cổng số hóa di tích và không gian văn hóa Xã Như Quỳnh, tỉnh Hưng Yên bằng công nghệ tham quan thực tế ảo VR360.
|
||||
</p>
|
||||
</div>
|
||||
|
||||
<nav className="grid gap-2 text-sm" aria-label="Liên kết nhanh">
|
||||
<nav className="grid content-start gap-2.5 text-sm" aria-label="Liên kết nhanh">
|
||||
<p className="font-bold text-[var(--tour-ink)]">Liên kết nhanh</p>
|
||||
{navigation.map((item) => (
|
||||
<Link key={item.href} href={item.href} className="w-fit text-[var(--muted-foreground)] transition-colors hover:text-[var(--tour-ink)]">
|
||||
<Link
|
||||
key={item.href}
|
||||
href={item.href}
|
||||
className="group inline-flex w-fit items-center gap-2 text-[var(--muted-foreground)] transition-colors hover:text-[var(--primary)]"
|
||||
>
|
||||
<span aria-hidden className="h-1 w-1 rotate-45 bg-[var(--primary)]/45 transition-transform group-hover:scale-150" />
|
||||
{item.label}
|
||||
</Link>
|
||||
))}
|
||||
</nav>
|
||||
|
||||
<div className="grid gap-3 text-sm leading-6 text-[var(--muted-foreground)] lg:text-right">
|
||||
<div className="grid content-start gap-3 text-sm leading-6 text-[var(--muted-foreground)] lg:text-right">
|
||||
<p className="font-bold text-[var(--tour-ink)]">Thông tin liên hệ</p>
|
||||
<p className="inline-flex items-start gap-2 lg:justify-end">
|
||||
<MapPin className="mt-0.5 h-4 w-4 shrink-0 text-[var(--primary)]" strokeWidth={1.8} />
|
||||
@@ -48,8 +60,10 @@ export function SiteFooter() {
|
||||
</Link>
|
||||
</div>
|
||||
</div>
|
||||
<div className="mx-auto mt-8 max-w-7xl border-t border-[var(--surface-border)] pt-5 text-center text-sm font-semibold text-[var(--muted-foreground)]">
|
||||
© 2026 VR360 Di sản Xã Như Quỳnh.
|
||||
|
||||
<div className="mx-auto mt-10 flex max-w-7xl flex-col items-center justify-between gap-2 border-t border-[var(--surface-border)] pt-5 text-sm font-semibold text-[var(--muted-foreground)] sm:flex-row">
|
||||
<p>© 2026 VR360 Di sản Xã Như Quỳnh.</p>
|
||||
<p className="text-[var(--muted-foreground)]/85">Chuyển đổi số di sản · giai đoạn 2026–2030</p>
|
||||
</div>
|
||||
</footer>
|
||||
);
|
||||
|
||||
@@ -0,0 +1,43 @@
|
||||
"use client";
|
||||
|
||||
import Link from "next/link";
|
||||
import { useEffect } from "react";
|
||||
import { Home, RotateCcw } from "lucide-react";
|
||||
import { HeritageSeal } from "@/app/components/landing/HeritageOrnament";
|
||||
|
||||
export default function Error({
|
||||
error,
|
||||
reset,
|
||||
}: {
|
||||
error: Error & { digest?: string };
|
||||
reset: () => void;
|
||||
}) {
|
||||
useEffect(() => {
|
||||
// Ghi log lỗi để theo dõi (có thể nối với dịch vụ giám sát sau này).
|
||||
console.error(error);
|
||||
}, [error]);
|
||||
|
||||
return (
|
||||
<main className="public-page flex min-h-[100dvh] flex-col items-center justify-center bg-[var(--background)] px-4 py-24 text-center text-[var(--foreground)] sm:px-6">
|
||||
<HeritageSeal className="h-16 w-16 text-[var(--primary)] opacity-85" />
|
||||
<p className="public-kicker public-kicker--center mt-7">Đã xảy ra sự cố</p>
|
||||
<h1 className="public-display public-heading-safe mt-3 text-[clamp(1.9rem,5vw,3rem)] font-bold text-[var(--tour-ink)]">
|
||||
Không thể tải nội dung
|
||||
</h1>
|
||||
<p className="mt-4 max-w-[52ch] text-base leading-7 text-[var(--muted-foreground)] sm:text-lg">
|
||||
Đã có lỗi không mong muốn khi hiển thị trang. Vui lòng thử lại; nếu sự cố tiếp diễn, hãy
|
||||
quay về trang chủ và truy cập lại sau ít phút.
|
||||
</p>
|
||||
<div className="mt-9 flex flex-wrap items-center justify-center gap-3">
|
||||
<button type="button" onClick={reset} className="public-cta">
|
||||
<RotateCcw className="h-4 w-4" strokeWidth={1.8} />
|
||||
Thử lại
|
||||
</button>
|
||||
<Link href="/" className="public-cta-secondary">
|
||||
<Home className="h-4 w-4" strokeWidth={1.8} />
|
||||
Về trang chủ
|
||||
</Link>
|
||||
</div>
|
||||
</main>
|
||||
);
|
||||
}
|
||||
+2
-2
@@ -636,8 +636,8 @@ a {
|
||||
}
|
||||
|
||||
.public-page .public-heading-safe {
|
||||
font-size: clamp(1.85rem, 8.7vw, 2.35rem);
|
||||
line-height: 1.12;
|
||||
font-size: clamp(1.6rem, 7.4vw, 2.15rem);
|
||||
line-height: 1.14;
|
||||
}
|
||||
|
||||
.public-page .public-hero-title {
|
||||
|
||||
@@ -0,0 +1,10 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 48 48" width="48" height="48">
|
||||
<rect width="48" height="48" rx="11" fill="#0e3a2c" />
|
||||
<g fill="none" stroke="#dcc07f" stroke-width="1.6" stroke-linejoin="round">
|
||||
<circle cx="24" cy="24" r="16.5" />
|
||||
<rect x="14" y="14" width="20" height="20" />
|
||||
<rect x="14" y="14" width="20" height="20" transform="rotate(45 24 24)" />
|
||||
<circle cx="24" cy="24" r="4" />
|
||||
</g>
|
||||
<circle cx="24" cy="24" r="1.7" fill="#dcc07f" />
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 491 B |
@@ -0,0 +1,46 @@
|
||||
import type { Metadata } from "next";
|
||||
import Link from "next/link";
|
||||
import { Compass, Home } from "lucide-react";
|
||||
import { HeritageSeal } from "@/app/components/landing/HeritageOrnament";
|
||||
import { SiteFooter } from "@/app/components/landing/SiteFooter";
|
||||
import { SiteHeader } from "@/app/components/landing/SiteHeader";
|
||||
|
||||
export const metadata: Metadata = {
|
||||
title: "Không tìm thấy trang | VR360 Như Quỳnh",
|
||||
description: "Trang bạn tìm không tồn tại hoặc đã được di chuyển.",
|
||||
};
|
||||
|
||||
export default function NotFound() {
|
||||
return (
|
||||
<main className="public-page flex min-h-[100dvh] flex-col bg-[var(--background)] text-[var(--foreground)]">
|
||||
<SiteHeader />
|
||||
|
||||
<section className="mx-auto flex w-full max-w-3xl flex-1 flex-col items-center justify-center px-4 py-24 text-center sm:px-6">
|
||||
<HeritageSeal className="h-16 w-16 text-[var(--primary)] opacity-85" />
|
||||
<p className="public-display mt-7 text-[clamp(3.5rem,12vw,6rem)] font-bold leading-none text-[var(--tour-ink)]">
|
||||
404
|
||||
</p>
|
||||
<p className="public-kicker public-kicker--center mt-6">Không tìm thấy trang</p>
|
||||
<h1 className="public-display public-heading-safe mt-3 text-[clamp(1.7rem,4vw,2.5rem)] font-bold text-[var(--tour-ink)]">
|
||||
Trang này chưa được số hóa
|
||||
</h1>
|
||||
<p className="mt-4 max-w-[52ch] text-base leading-7 text-[var(--muted-foreground)] sm:text-lg">
|
||||
Đường dẫn bạn truy cập có thể đã thay đổi hoặc nội dung đang được bổ sung. Hãy quay về
|
||||
trang chủ hoặc khám phá hệ thống di tích của Xã Như Quỳnh.
|
||||
</p>
|
||||
<div className="mt-9 flex flex-wrap items-center justify-center gap-3">
|
||||
<Link href="/" className="public-cta">
|
||||
<Home className="h-4 w-4" strokeWidth={1.8} />
|
||||
Về trang chủ
|
||||
</Link>
|
||||
<Link href="/di-tich" className="public-cta-secondary">
|
||||
<Compass className="h-4 w-4" strokeWidth={1.8} />
|
||||
Khám phá di tích
|
||||
</Link>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<SiteFooter />
|
||||
</main>
|
||||
);
|
||||
}
|
||||
+13
-1
@@ -269,7 +269,7 @@ export default function Home() {
|
||||
</div>
|
||||
|
||||
<div className="mt-10 grid gap-3 sm:grid-cols-2 lg:grid-cols-3">
|
||||
{upcomingSites.map((site) => (
|
||||
{upcomingSites.slice(0, 6).map((site) => (
|
||||
<Link
|
||||
key={site.id}
|
||||
href={site.detailUrl}
|
||||
@@ -290,6 +290,18 @@ export default function Home() {
|
||||
</Link>
|
||||
))}
|
||||
</div>
|
||||
|
||||
{upcomingSites.length > 6 && (
|
||||
<div className="mt-8 flex justify-center">
|
||||
<Link
|
||||
href="/di-tich"
|
||||
className="inline-flex items-center gap-2 rounded-full border border-[var(--surface-border)] bg-[var(--surface-band)] px-6 py-2.5 text-sm font-bold text-[var(--tour-ink)] transition hover:border-[rgb(200_164_106_/_0.5)] hover:text-[var(--primary)]"
|
||||
>
|
||||
Xem thêm {upcomingSites.length - 6} di tích khác
|
||||
<ArrowRight className="h-4 w-4" strokeWidth={1.8} />
|
||||
</Link>
|
||||
</div>
|
||||
)}
|
||||
</section>
|
||||
|
||||
<section className="border-y border-[var(--surface-border)] bg-[var(--surface-band)]">
|
||||
|
||||
Reference in New Issue
Block a user