203 lines
8.4 KiB
TypeScript
203 lines
8.4 KiB
TypeScript
'use client';
|
|
|
|
import { useState, useEffect } from 'react';
|
|
import { useRouter } from 'next/navigation';
|
|
import { useTranslation } from 'react-i18next';
|
|
import { Button } from '@/app/components/ui/button';
|
|
import { Card, CardContent } from '@/app/components/ui/card';
|
|
import { Badge } from '@/app/components/ui/badge';
|
|
import { Skeleton } from '@/app/components/ui/skeleton';
|
|
import { ArrowLeft, Calendar, User, Tag, Phone, Mail, MessageSquare } from 'lucide-react';
|
|
import { formatDate } from '@/lib/utils';
|
|
|
|
interface ConsultationDetailPageClientProps {
|
|
consultationId: string;
|
|
locale: string;
|
|
}
|
|
|
|
interface Consultation {
|
|
id: string;
|
|
metadata: {
|
|
title: string;
|
|
date: string;
|
|
author: string;
|
|
category: string;
|
|
status: string;
|
|
contact: {
|
|
name: string;
|
|
phone: string;
|
|
email: string;
|
|
message: string;
|
|
};
|
|
};
|
|
}
|
|
|
|
export default function ConsultationDetailPageClient({
|
|
consultationId,
|
|
locale,
|
|
}: ConsultationDetailPageClientProps) {
|
|
const router = useRouter();
|
|
const { t } = useTranslation(['consultation', 'common']);
|
|
const [consultation, setConsultation] = useState<Consultation | null>(null);
|
|
const [isLoading, setIsLoading] = useState(true);
|
|
const [error, setError] = useState<string | null>(null);
|
|
|
|
useEffect(() => {
|
|
const fetchConsultation = async () => {
|
|
try {
|
|
const response = await fetch(
|
|
`/api/consultations/${consultationId}?locale=${locale}`,
|
|
);
|
|
if (!response.ok) {
|
|
if (response.status === 404) {
|
|
throw new Error(t('consultation.error.notFound'));
|
|
}
|
|
throw new Error(t('consultation.error.fetchFailed'));
|
|
}
|
|
const data = await response.json();
|
|
setConsultation(data.consultation);
|
|
} catch (err) {
|
|
setError(err instanceof Error ? err.message : t('consultation.error.unknown'));
|
|
} finally {
|
|
setIsLoading(false);
|
|
}
|
|
};
|
|
|
|
fetchConsultation();
|
|
}, [consultationId, locale, t]);
|
|
|
|
// 渲染加载状态
|
|
const renderSkeleton = () => (
|
|
<div className="container mx-auto px-4 py-8" data-oid="sxmuh4m">
|
|
<div className="max-w-4xl mx-auto" data-oid="nvvi7m3">
|
|
<Skeleton className="h-12 w-3/4 mb-8" data-oid="rjja.9:" />
|
|
<div className="space-y-4 mb-8" data-oid=":elb21i">
|
|
<Skeleton className="h-6 w-24" data-oid="7hbea.3" />
|
|
<Skeleton className="h-6 w-24" data-oid="j--iy-s" />
|
|
<Skeleton className="h-6 w-24" data-oid="huo.du2" />
|
|
</div>
|
|
<div className="space-y-4" data-oid="-.-t5x6">
|
|
<Skeleton className="h-4 w-full" data-oid=":-y-sy:" />
|
|
<Skeleton className="h-4 w-full" data-oid="lexxi27" />
|
|
<Skeleton className="h-4 w-3/4" data-oid="lk3ih5q" />
|
|
</div>
|
|
</div>
|
|
</div>
|
|
);
|
|
|
|
// 渲染错误状态
|
|
const renderError = () => (
|
|
<div className="container mx-auto px-4 py-8" data-oid="nk0sm3m">
|
|
<div className="max-w-4xl mx-auto text-center" data-oid="wkl4avi">
|
|
<h2 className="text-2xl font-bold text-red-600 mb-4" data-oid="al5.d3h">
|
|
{t('consultation.error.title')}
|
|
</h2>
|
|
<p className="text-gray-600 mb-6" data-oid="inkewhh">
|
|
{error}
|
|
</p>
|
|
<Button onClick={() => router.back()} variant="outline" data-oid="7qp7p7o">
|
|
{t('consultation.error.goBack')}
|
|
</Button>
|
|
</div>
|
|
</div>
|
|
);
|
|
|
|
if (isLoading) {
|
|
return renderSkeleton();
|
|
}
|
|
|
|
if (error || !consultation) {
|
|
return renderError();
|
|
}
|
|
|
|
return (
|
|
<div className="container mx-auto px-4 py-8" data-oid="necnv9y">
|
|
<div className="max-w-4xl mx-auto" data-oid="m43sd-3">
|
|
{/* 返回按钮 */}
|
|
<Button
|
|
variant="ghost"
|
|
className="mb-8"
|
|
onClick={() => router.back()}
|
|
data-oid="h5u87_m"
|
|
>
|
|
<ArrowLeft className="mr-2" size={20} data-oid="w8ntmqo" />
|
|
{t('consultation.backToList')}
|
|
</Button>
|
|
|
|
{/* 咨询标题 */}
|
|
<h1 className="text-4xl font-bold mb-8" data-oid="iq340lp">
|
|
{consultation.metadata.title}
|
|
</h1>
|
|
|
|
{/* 咨询元数据 */}
|
|
<div
|
|
className="flex flex-wrap items-center gap-4 mb-8 text-gray-600"
|
|
data-oid="gz-1uwt"
|
|
>
|
|
<div className="flex items-center" data-oid="snd80xz">
|
|
<Calendar className="mr-2" size={16} data-oid="0_v45vt" />
|
|
{formatDate(consultation.metadata.date, locale)}
|
|
</div>
|
|
<div className="flex items-center" data-oid="4u.95u-">
|
|
<User className="mr-2" size={16} data-oid="ba_jol7" />
|
|
{consultation.metadata.author}
|
|
</div>
|
|
<div className="flex items-center" data-oid="j_am5.w">
|
|
<Tag className="mr-2" size={16} data-oid="8dfyx_o" />
|
|
<Badge variant="secondary" data-oid="_:-v0x6">
|
|
{t(`consultation.categories.${consultation.metadata.category}`)}
|
|
</Badge>
|
|
</div>
|
|
<div className="flex items-center" data-oid="v9kzwmm">
|
|
<Badge
|
|
variant={
|
|
consultation.metadata.status === 'pending'
|
|
? 'outline'
|
|
: consultation.metadata.status === 'in_progress'
|
|
? 'secondary'
|
|
: 'default'
|
|
}
|
|
data-oid=":8-06ys"
|
|
>
|
|
{t(`consultation.status.${consultation.metadata.status}`)}
|
|
</Badge>
|
|
</div>
|
|
</div>
|
|
|
|
{/* 联系信息 */}
|
|
<Card className="mb-8" data-oid="8uha3r3">
|
|
<CardContent className="p-6" data-oid="-mib74k">
|
|
<h2 className="text-xl font-semibold mb-4" data-oid="2n.4k5h">
|
|
{t('consultation.contactInfo')}
|
|
</h2>
|
|
<div className="space-y-4" data-oid="4:.ptqu">
|
|
<div className="flex items-center" data-oid="_u0178p">
|
|
<User className="mr-2" size={16} data-oid="8dbq.i-" />
|
|
<span data-oid="nxaq1w8">{consultation.metadata.contact.name}</span>
|
|
</div>
|
|
<div className="flex items-center" data-oid=":3s8nk7">
|
|
<Phone className="mr-2" size={16} data-oid="nj78b25" />
|
|
<span data-oid="7yj8j61">
|
|
{consultation.metadata.contact.phone}
|
|
</span>
|
|
</div>
|
|
<div className="flex items-center" data-oid="jmg55i2">
|
|
<Mail className="mr-2" size={16} data-oid="lsc_6:d" />
|
|
<span data-oid="c5t2gbe">
|
|
{consultation.metadata.contact.email}
|
|
</span>
|
|
</div>
|
|
<div className="flex items-start" data-oid="rdyas_p">
|
|
<MessageSquare className="mr-2 mt-1" size={16} data-oid="0_30ig-" />
|
|
<span className="whitespace-pre-wrap" data-oid="4v0sw8j">
|
|
{consultation.metadata.contact.message}
|
|
</span>
|
|
</div>
|
|
</div>
|
|
</CardContent>
|
|
</Card>
|
|
</div>
|
|
</div>
|
|
);
|
|
}
|