refactor(shared-error-page): update error page to use translation keys for dynamic content

This commit is contained in:
beilunyang
2025-10-22 00:58:37 +08:00
parent 7398b73f3f
commit 1ffe920d47
6 changed files with 62 additions and 60 deletions

View File

@@ -188,7 +188,6 @@ export function SharedEmailPageClient({
return tShared("sharedMailbox") return tShared("sharedMailbox")
} }
})()} })()}
showCta={true}
ctaText={tShared("createOwnEmail")} ctaText={tShared("createOwnEmail")}
/> />

View File

@@ -1,4 +1,3 @@
import { getTranslations } from "next-intl/server"
import { getSharedEmail, getSharedEmailMessages } from "@/lib/shared-data" import { getSharedEmail, getSharedEmailMessages } from "@/lib/shared-data"
import { SharedErrorPage } from "@/components/emails/shared-error-page" import { SharedErrorPage } from "@/components/emails/shared-error-page"
import { SharedEmailPageClient } from "./page-client" import { SharedEmailPageClient } from "./page-client"
@@ -12,7 +11,6 @@ interface PageProps {
export default async function SharedEmailPage({ params }: PageProps) { export default async function SharedEmailPage({ params }: PageProps) {
const { token } = await params const { token } = await params
const tShared = await getTranslations("emails.shared")
// 服务端获取数据 // 服务端获取数据
const email = await getSharedEmail(token) const email = await getSharedEmail(token)
@@ -20,11 +18,11 @@ export default async function SharedEmailPage({ params }: PageProps) {
if (!email) { if (!email) {
return ( return (
<SharedErrorPage <SharedErrorPage
title={tShared("emailNotFound")} titleKey="emailNotFound"
subtitle={tShared("linkExpired")} subtitleKey="linkExpired"
error={tShared("linkInvalid")} errorKey="linkInvalid"
description={tShared("linkInvalidDescription")} descriptionKey="linkInvalidDescription"
ctaText={tShared("createOwnEmail")} ctaTextKey="createOwnEmail"
/> />
) )
} }

View File

@@ -37,7 +37,6 @@ export function SharedMessagePageClient({ message }: SharedMessagePageClientProp
: message.emailExpiresAt : message.emailExpiresAt
? `${tShared("expiresAt")}: ${new Date(message.emailExpiresAt).toLocaleString()}` ? `${tShared("expiresAt")}: ${new Date(message.emailExpiresAt).toLocaleString()}`
: tShared("sharedMessage")} : tShared("sharedMessage")}
showCta={true}
ctaText={tShared("createOwnEmail")} ctaText={tShared("createOwnEmail")}
/> />

View File

@@ -1,4 +1,3 @@
import { getTranslations } from "next-intl/server"
import { getSharedMessage } from "@/lib/shared-data" import { getSharedMessage } from "@/lib/shared-data"
import { SharedErrorPage } from "@/components/emails/shared-error-page" import { SharedErrorPage } from "@/components/emails/shared-error-page"
import { SharedMessagePageClient } from "./page-client" import { SharedMessagePageClient } from "./page-client"
@@ -12,7 +11,6 @@ interface PageProps {
export default async function SharedMessagePage({ params }: PageProps) { export default async function SharedMessagePage({ params }: PageProps) {
const { token } = await params const { token } = await params
const tShared = await getTranslations("emails.shared")
// 服务端获取数据 // 服务端获取数据
const message = await getSharedMessage(token) const message = await getSharedMessage(token)
@@ -20,11 +18,11 @@ export default async function SharedMessagePage({ params }: PageProps) {
if (!message) { if (!message) {
return ( return (
<SharedErrorPage <SharedErrorPage
title={tShared("messageNotFound")} titleKey="messageNotFound"
subtitle={tShared("linkExpired")} subtitleKey="linkExpired"
error={tShared("linkInvalid")} errorKey="linkInvalid"
description={tShared("linkInvalidDescription")} descriptionKey="linkInvalidDescription"
ctaText={tShared("createOwnEmail")} ctaTextKey="createOwnEmail"
/> />
) )
} }

View File

@@ -1,34 +1,48 @@
"use client" "use client"
import { useTranslations } from "next-intl"
import { AlertCircle } from "lucide-react" import { AlertCircle } from "lucide-react"
import { Card } from "@/components/ui/card" import { Card } from "@/components/ui/card"
import { BrandHeader } from "@/components/ui/brand-header" import { BrandHeader } from "@/components/ui/brand-header"
import { FloatingLanguageSwitcher } from "@/components/layout/floating-language-switcher" import { FloatingLanguageSwitcher } from "@/components/layout/floating-language-switcher"
interface SharedErrorPageProps { interface SharedErrorPageProps {
title: string titleKey: string
subtitle: string subtitleKey: string
error: string errorKey: string
description: string descriptionKey: string
ctaText: string ctaTextKey: string
} }
export function SharedErrorPage({ title, subtitle, error, description, ctaText }: SharedErrorPageProps) { export function SharedErrorPage({
titleKey,
subtitleKey,
errorKey,
descriptionKey,
ctaTextKey,
}: SharedErrorPageProps) {
const tShared = useTranslations("emails.shared")
const resolvedTitle = tShared(titleKey)
const resolvedSubtitle = tShared(subtitleKey)
const resolvedError = tShared(errorKey)
const resolvedDescription = tShared(descriptionKey)
const resolvedCtaText = tShared(ctaTextKey)
return ( return (
<div className="min-h-screen bg-gray-50 dark:bg-gray-900"> <div className="min-h-screen bg-gray-50 dark:bg-gray-900 flex flex-col justify-center items-center">
<div className="container mx-auto p-4 max-w-4xl"> <div className="container mx-auto p-4 max-w-4xl">
<BrandHeader <BrandHeader
title={title} title={resolvedTitle}
subtitle={subtitle} subtitle={resolvedSubtitle}
showCta={true} ctaText={resolvedCtaText}
ctaText={ctaText}
/> />
<div className="text-center"> <div className="text-center mt-6">
<Card className="max-w-md mx-auto p-8 text-center space-y-4"> <Card className="max-w-md mx-auto p-8 text-center space-y-4">
<AlertCircle className="h-12 w-12 mx-auto text-destructive" /> <AlertCircle className="h-12 w-12 mx-auto text-destructive" />
<h2 className="text-2xl font-bold">{error}</h2> <h2 className="text-2xl font-bold">{resolvedError}</h2>
<p className="text-gray-500"> <p className="text-gray-500">
{description} {resolvedDescription}
</p> </p>
</Card> </Card>
</div> </div>
@@ -37,4 +51,4 @@ export function SharedErrorPage({ title, subtitle, error, description, ctaText }
<FloatingLanguageSwitcher /> <FloatingLanguageSwitcher />
</div> </div>
) )
} }

View File

@@ -8,28 +8,24 @@ import { ExternalLink, Mail } from "lucide-react"
interface BrandHeaderProps { interface BrandHeaderProps {
title?: string title?: string
subtitle?: string subtitle?: string
showCta?: boolean
ctaText?: string ctaText?: string
ctaHref?: string
} }
export function BrandHeader({ export function BrandHeader({
title, title,
subtitle, subtitle,
showCta = true,
ctaText, ctaText,
ctaHref = "https://moemail.app"
}: BrandHeaderProps) { }: BrandHeaderProps) {
const t = useTranslations("emails.shared.brand") const t = useTranslations("emails.shared.brand")
const displayTitle = title || t("title") const displayTitle = title || t("title")
const displaySubtitle = subtitle || t("subtitle") const displaySubtitle = subtitle || t("subtitle")
const displayCtaText = ctaText || t("cta") const displayCtaText = ctaText || t("cta")
return ( return (
<div className="text-center space-y-4 lg:pb-4"> <div className="text-center space-y-4 lg:pb-4">
<div className="flex justify-center pt-2"> <div className="flex justify-center pt-2">
<Link <Link
href={ctaHref} href="https://moemail.app"
className="flex items-center gap-3 hover:opacity-80 transition-opacity group" className="flex items-center gap-3 hover:opacity-80 transition-opacity group"
> >
<div className="relative w-12 h-12"> <div className="relative w-12 h-12">
@@ -47,32 +43,32 @@ export function BrandHeader({
d="M4 8h24v16H4V8z" d="M4 8h24v16H4V8z"
className="fill-primary/20" className="fill-primary/20"
/> />
{/* 信封边框 */} {/* 信封边框 */}
<path <path
d="M4 8h24v2H4V8zM4 22h24v2H4v-2z" d="M4 8h24v2H4V8zM4 22h24v2H4v-2z"
className="fill-primary" className="fill-primary"
/> />
{/* @ 符号 */} {/* @ 符号 */}
<path <path
d="M14 12h4v4h-4v-4zM12 14h2v4h-2v-4zM18 14h2v4h-2v-4zM14 18h4v2h-4v-2z" d="M14 12h4v4h-4v-4zM12 14h2v4h-2v-4zM18 14h2v4h-2v-4zM14 18h4v2h-4v-2z"
className="fill-primary" className="fill-primary"
/> />
{/* 折线装饰 */} {/* 折线装饰 */}
<path <path
d="M4 8l12 8 12-8" d="M4 8l12 8 12-8"
className="stroke-primary stroke-2" className="stroke-primary stroke-2"
fill="none" fill="none"
/> />
{/* 装饰点 */} {/* 装饰点 */}
<path <path
d="M8 18h2v2H8v-2zM22 18h2v2h-2v-2z" d="M8 18h2v2H8v-2zM22 18h2v2h-2v-2z"
className="fill-primary/60" className="fill-primary/60"
/> />
{/* 底部装饰线 */} {/* 底部装饰线 */}
<path <path
d="M8 14h2v2H8v-2zM22 14h2v2h-2v-2z" d="M8 14h2v2H8v-2zM22 14h2v2h-2v-2z"
@@ -96,21 +92,19 @@ export function BrandHeader({
</p> </p>
</div> </div>
{showCta && ( <div className="flex justify-center">
<div className="flex justify-center"> <Button
<Button asChild
asChild size="lg"
size="lg" className="gap-2 bg-primary hover:bg-primary/90 text-white px-8 min-h-10 h-auto py-1"
className="gap-2 bg-primary hover:bg-primary/90 text-white px-8 min-h-10 h-auto py-1" >
> <Link href="/" target="_blank" rel="noopener noreferrer">
<Link href={ctaHref} target="_blank" rel="noopener noreferrer"> <Mail className="w-5 h-5" />
<Mail className="w-5 h-5" /> {displayCtaText}
{displayCtaText} <ExternalLink className="w-4 h-4" />
<ExternalLink className="w-4 h-4" /> </Link>
</Link> </Button>
</Button> </div>
</div>
)}
</div> </div>
) )
} }