feat: 添加E2E测试并优化Docker部署配置
- 新增Playwright E2E测试配置和测试脚本 - 优化Dockerfile和docker-compose.yml配置 - 新增novalon-nginx和novalon-website的docker-compose配置 - 优化contact页面和contact-section组件的代码结构 - 更新多个页面的SEO和元数据配置 - 添加备案图标资源 - 修复ESLint错误:转义引号、添加ESLint禁用注释、移除未使用变量 测试覆盖: 新增website-acceptance.spec.ts E2E测试
This commit is contained in:
@@ -7,7 +7,7 @@ import { COMPANY_INFO, STATS } from '@/lib/constants';
|
||||
import { Card, CardContent } from '@/components/ui/card';
|
||||
import { PageHeader } from '@/components/ui/page-header';
|
||||
import { FlipClock } from '@/components/ui/flip-clock';
|
||||
import { Lightbulb, Users, Target, Award, MapPin, Mail, Phone } from 'lucide-react';
|
||||
import { Lightbulb, Users, Target, Award, MapPin, Mail } from 'lucide-react';
|
||||
|
||||
export function AboutClient() {
|
||||
const contentRef = useRef(null);
|
||||
@@ -115,7 +115,7 @@ export function AboutClient() {
|
||||
<div>
|
||||
<h3 className="text-xl font-semibold text-[#1C1C1C] mb-3">成长伙伴</h3>
|
||||
<p className="text-[#5C5C5C] mb-3 leading-relaxed">
|
||||
我们不把"项目交付"当作终点。
|
||||
我们不把“项目交付”当作终点。
|
||||
</p>
|
||||
<p className="text-[#5C5C5C] mb-3 leading-relaxed">
|
||||
您的业务增长了吗?您的团队能力提升了吗?
|
||||
@@ -124,7 +124,7 @@ export function AboutClient() {
|
||||
您下一次遇到难题时,还会第一个想到我们吗?
|
||||
</p>
|
||||
<p className="text-[#5C5C5C] mt-3 leading-relaxed">
|
||||
这些问题,比"项目是否按时交付"更让我们在意。
|
||||
这些问题,比“项目是否按时交付”更让我们在意。
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
@@ -145,7 +145,7 @@ export function AboutClient() {
|
||||
</li>
|
||||
<li className="flex items-start gap-3">
|
||||
<span className="text-green-600 font-bold">✅</span>
|
||||
<span className="text-[#5C5C5C]">不做路过就忘的"一锤子买卖"</span>
|
||||
<span className="text-[#5C5C5C]">不做路过就忘的“一锤子买卖”</span>
|
||||
</li>
|
||||
</ul>
|
||||
<p className="text-[#5C5C5C] leading-relaxed font-medium">
|
||||
@@ -257,15 +257,7 @@ export function AboutClient() {
|
||||
<p className="text-sm font-medium text-[#1C1C1C]">{COMPANY_INFO.email}</p>
|
||||
</div>
|
||||
</div>
|
||||
<div className="flex items-center gap-3">
|
||||
<div className="p-2 bg-white rounded-lg">
|
||||
<Phone className="w-5 h-5 text-[#C41E3A]" />
|
||||
</div>
|
||||
<div>
|
||||
<p className="text-sm text-[#5C5C5C]">联系电话</p>
|
||||
<p className="text-sm font-medium text-[#1C1C1C]">{COMPANY_INFO.phone}</p>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
</motion.div>
|
||||
</motion.div>
|
||||
|
||||
@@ -8,7 +8,7 @@ import { Textarea } from '@/components/ui/textarea';
|
||||
import { Toast } from '@/components/ui/toast';
|
||||
import { sanitizeInput } from '@/lib/sanitize';
|
||||
import { generateCSRFToken, setCSRFTokenToStorage } from '@/lib/csrf';
|
||||
import { Mail, Phone, MapPin, Send, Loader2, Clock, HeadphonesIcon, CheckCircle2 } from 'lucide-react';
|
||||
import { Mail, MapPin, Send, Loader2, Clock, HeadphonesIcon, CheckCircle2 } from 'lucide-react';
|
||||
import { COMPANY_INFO } from '@/lib/constants';
|
||||
import { submitContactForm, ContactFormState } from './actions';
|
||||
|
||||
@@ -55,32 +55,35 @@ export default function ContactPage() {
|
||||
const isSubmitting = isPending;
|
||||
|
||||
useEffect(() => {
|
||||
setIsVisible(true);
|
||||
|
||||
const token = generateCSRFToken();
|
||||
setCsrfToken(token);
|
||||
setCSRFTokenToStorage(token);
|
||||
requestAnimationFrame(() => {
|
||||
setIsVisible(true);
|
||||
const token = generateCSRFToken();
|
||||
setCsrfToken(token);
|
||||
setCSRFTokenToStorage(token);
|
||||
});
|
||||
}, []);
|
||||
|
||||
useEffect(() => {
|
||||
if (state) {
|
||||
if (state.success) {
|
||||
setToastMessage(state.message || '表单提交成功!我们会尽快与您联系。');
|
||||
setToastType('success');
|
||||
setShowToast(true);
|
||||
|
||||
const newToken = generateCSRFToken();
|
||||
setCsrfToken(newToken);
|
||||
setCSRFTokenToStorage(newToken);
|
||||
} else if (state.error) {
|
||||
setToastMessage(state.error);
|
||||
setToastType('error');
|
||||
setShowToast(true);
|
||||
|
||||
if (state.errors) {
|
||||
setErrors(state.errors);
|
||||
requestAnimationFrame(() => {
|
||||
if (state.success) {
|
||||
setToastMessage(state.message || '表单提交成功!我们会尽快与您联系。');
|
||||
setToastType('success');
|
||||
setShowToast(true);
|
||||
|
||||
const newToken = generateCSRFToken();
|
||||
setCsrfToken(newToken);
|
||||
setCSRFTokenToStorage(newToken);
|
||||
} else if (state.error) {
|
||||
setToastMessage(state.error);
|
||||
setToastType('error');
|
||||
setShowToast(true);
|
||||
|
||||
if (state.errors) {
|
||||
setErrors(state.errors);
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
}, [state]);
|
||||
|
||||
@@ -161,7 +164,7 @@ export default function ContactPage() {
|
||||
`}
|
||||
>
|
||||
<div className="flex items-center gap-3 mb-4">
|
||||
<div className="w-8 h-px bg-gradient-to-r from-[#1C1C1C] to-[#C41E3A]" />
|
||||
<div className="w-8 h-px bg-linear-to-r from-[#1C1C1C] to-[#C41E3A]" />
|
||||
<span className="text-sm text-[#5C5C5C] tracking-wide" data-testid="page-badge">联系我们</span>
|
||||
</div>
|
||||
<h1 className="text-4xl md:text-5xl font-bold text-[#1C1C1C] mb-4">
|
||||
@@ -184,7 +187,7 @@ export default function ContactPage() {
|
||||
<h2 className="text-lg font-semibold text-[#1C1C1C] mb-6">联系方式</h2>
|
||||
<div className="space-y-4" data-testid="contact-info">
|
||||
<div className="flex items-start gap-4 group" data-testid="email-info">
|
||||
<div className="w-10 h-10 bg-[#C41E3A] rounded-md flex items-center justify-center flex-shrink-0 transition-transform duration-200 group-hover:scale-105">
|
||||
<div className="w-10 h-10 bg-[#C41E3A] rounded-md flex items-center justify-center shrink-0 transition-transform duration-200 group-hover:scale-105">
|
||||
<Mail className="w-5 h-5 text-white" />
|
||||
</div>
|
||||
<div>
|
||||
@@ -194,19 +197,9 @@ export default function ContactPage() {
|
||||
</a>
|
||||
</div>
|
||||
</div>
|
||||
<div className="flex items-start gap-4 group" data-testid="phone-info">
|
||||
<div className="w-10 h-10 bg-[#C41E3A] rounded-md flex items-center justify-center flex-shrink-0 transition-transform duration-200 group-hover:scale-105">
|
||||
<Phone className="w-5 h-5 text-white" />
|
||||
</div>
|
||||
<div>
|
||||
<p className="text-sm text-[#5C5C5C] mb-1">电话</p>
|
||||
<a href={`tel:${COMPANY_INFO.phone}`} className="text-[#1C1C1C] hover:text-[#C41E3A] transition-colors duration-200" data-testid="phone-link">
|
||||
{COMPANY_INFO.phone}
|
||||
</a>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div className="flex items-start gap-4 group" data-testid="address-info">
|
||||
<div className="w-10 h-10 bg-[#C41E3A] rounded-md flex items-center justify-center flex-shrink-0 transition-transform duration-200 group-hover:scale-105">
|
||||
<div className="w-10 h-10 bg-[#C41E3A] rounded-md flex items-center justify-center shrink-0 transition-transform duration-200 group-hover:scale-105">
|
||||
<MapPin className="w-5 h-5 text-white" />
|
||||
</div>
|
||||
<div>
|
||||
@@ -237,15 +230,15 @@ export default function ContactPage() {
|
||||
</div>
|
||||
<div className="space-y-3">
|
||||
<div className="flex items-start gap-2">
|
||||
<div className="w-1.5 h-1.5 bg-[#C41E3A] rounded-full mt-2 flex-shrink-0" />
|
||||
<div className="w-1.5 h-1.5 bg-[#C41E3A] rounded-full mt-2 shrink-0" />
|
||||
<p className="text-sm text-[#5C5C5C]">工作日 2 小时内快速响应您的咨询</p>
|
||||
</div>
|
||||
<div className="flex items-start gap-2">
|
||||
<div className="w-1.5 h-1.5 bg-[#C41E3A] rounded-full mt-2 flex-shrink-0" />
|
||||
<div className="w-1.5 h-1.5 bg-[#C41E3A] rounded-full mt-2 shrink-0" />
|
||||
<p className="text-sm text-[#5C5C5C]">提供免费的业务咨询和方案评估服务</p>
|
||||
</div>
|
||||
<div className="flex items-start gap-2">
|
||||
<div className="w-1.5 h-1.5 bg-[#C41E3A] rounded-full mt-2 flex-shrink-0" />
|
||||
<div className="w-1.5 h-1.5 bg-[#C41E3A] rounded-full mt-2 shrink-0" />
|
||||
<p className="text-sm text-[#5C5C5C]">根据您的需求量身定制最优解决方案</p>
|
||||
</div>
|
||||
</div>
|
||||
@@ -343,7 +336,7 @@ export default function ContactPage() {
|
||||
type="submit"
|
||||
data-testid="submit-button"
|
||||
size="lg"
|
||||
className="w-full group mt-auto min-h-[52px] md:min-h-0"
|
||||
className="w-full group mt-auto min-h-13 md:min-h-0"
|
||||
disabled={isSubmitting}
|
||||
>
|
||||
{isSubmitting ? (
|
||||
|
||||
Reference in New Issue
Block a user