첫 커밋
This commit is contained in:
318
src/pages/Contact.tsx
Normal file
318
src/pages/Contact.tsx
Normal file
@@ -0,0 +1,318 @@
|
||||
import React, { useState } from 'react';
|
||||
|
||||
interface FormData {
|
||||
name: string;
|
||||
email: string;
|
||||
phone: string;
|
||||
subject: string;
|
||||
message: string;
|
||||
}
|
||||
|
||||
interface FormErrors {
|
||||
name?: string;
|
||||
email?: string;
|
||||
phone?: string;
|
||||
subject?: string;
|
||||
message?: string;
|
||||
}
|
||||
|
||||
const Contact: React.FC = () => {
|
||||
const [formData, setFormData] = useState<FormData>({
|
||||
name: '',
|
||||
email: '',
|
||||
phone: '',
|
||||
subject: '',
|
||||
message: '',
|
||||
});
|
||||
|
||||
const [errors, setErrors] = useState<FormErrors>({});
|
||||
const [isSubmitting, setIsSubmitting] = useState(false);
|
||||
const [isSubmitted, setIsSubmitted] = useState(false);
|
||||
|
||||
const validateForm = (): boolean => {
|
||||
const newErrors: FormErrors = {};
|
||||
|
||||
if (!formData.name.trim()) {
|
||||
newErrors.name = '이름을 입력해주세요.';
|
||||
}
|
||||
|
||||
if (!formData.email.trim()) {
|
||||
newErrors.email = '이메일을 입력해주세요.';
|
||||
} else if (!/^[^\s@]+@[^\s@]+\.[^\s@]+$/.test(formData.email)) {
|
||||
newErrors.email = '올바른 이메일 형식을 입력해주세요.';
|
||||
}
|
||||
|
||||
if (!formData.phone.trim()) {
|
||||
newErrors.phone = '전화번호를 입력해주세요.';
|
||||
} else if (!/^[0-9-+\s()]+$/.test(formData.phone)) {
|
||||
newErrors.phone = '올바른 전화번호 형식을 입력해주세요.';
|
||||
}
|
||||
|
||||
if (!formData.subject.trim()) {
|
||||
newErrors.subject = '문의 제목을 입력해주세요.';
|
||||
}
|
||||
|
||||
if (!formData.message.trim()) {
|
||||
newErrors.message = '문의 내용을 입력해주세요.';
|
||||
} else if (formData.message.length < 10) {
|
||||
newErrors.message = '문의 내용을 최소 10자 이상 입력해주세요.';
|
||||
}
|
||||
|
||||
setErrors(newErrors);
|
||||
return Object.keys(newErrors).length === 0;
|
||||
};
|
||||
|
||||
const handleChange = (e: React.ChangeEvent<HTMLInputElement | HTMLTextAreaElement | HTMLSelectElement>) => {
|
||||
const { name, value } = e.target;
|
||||
setFormData(prev => ({
|
||||
...prev,
|
||||
[name]: value,
|
||||
}));
|
||||
|
||||
if (errors[name as keyof FormErrors]) {
|
||||
setErrors(prev => ({
|
||||
...prev,
|
||||
[name]: undefined,
|
||||
}));
|
||||
}
|
||||
};
|
||||
|
||||
const handleSubmit = async (e: React.FormEvent) => {
|
||||
e.preventDefault();
|
||||
|
||||
if (!validateForm()) return;
|
||||
|
||||
setIsSubmitting(true);
|
||||
|
||||
try {
|
||||
// 실제 구현에서는 API 호출
|
||||
await new Promise(resolve => setTimeout(resolve, 1000));
|
||||
|
||||
setIsSubmitted(true);
|
||||
setFormData({
|
||||
name: '',
|
||||
email: '',
|
||||
phone: '',
|
||||
subject: '',
|
||||
message: '',
|
||||
});
|
||||
} catch (error) {
|
||||
console.error('Form submission error:', error);
|
||||
} finally {
|
||||
setIsSubmitting(false);
|
||||
}
|
||||
};
|
||||
|
||||
if (isSubmitted) {
|
||||
return (
|
||||
<div className="min-h-screen bg-gray-50 flex items-center justify-center">
|
||||
<div className="bg-white p-8 rounded-lg shadow-lg max-w-md w-full mx-4">
|
||||
<div className="text-center">
|
||||
<div className="w-16 h-16 bg-green-100 rounded-full flex items-center justify-center mx-auto mb-4">
|
||||
<svg className="w-8 h-8 text-green-600" fill="none" stroke="currentColor" viewBox="0 0 24 24">
|
||||
<path strokeLinecap="round" strokeLinejoin="round" strokeWidth={2} d="M5 13l4 4L19 7" />
|
||||
</svg>
|
||||
</div>
|
||||
<h2 className="text-2xl font-bold text-gray-900 mb-2">
|
||||
문의가 접수되었습니다
|
||||
</h2>
|
||||
<p className="text-gray-600 mb-6">
|
||||
빠른 시일 내에 답변 드리겠습니다.
|
||||
</p>
|
||||
<button
|
||||
onClick={() => setIsSubmitted(false)}
|
||||
className="bg-blue-600 text-white px-6 py-2 rounded-md hover:bg-blue-700 transition-colors"
|
||||
>
|
||||
새 문의하기
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
return (
|
||||
<div className="min-h-screen bg-gray-50 py-12">
|
||||
<div className="container mx-auto px-4">
|
||||
<div className="max-w-4xl mx-auto">
|
||||
<div className="text-center mb-12">
|
||||
<h1 className="text-4xl font-bold text-gray-900 mb-4">
|
||||
문의하기
|
||||
</h1>
|
||||
<p className="text-lg text-gray-600">
|
||||
궁금한 사항이나 문의사항이 있으시면 언제든지 연락주세요.
|
||||
</p>
|
||||
</div>
|
||||
|
||||
<div className="grid md:grid-cols-2 gap-12">
|
||||
{/* Contact Information */}
|
||||
<div>
|
||||
<h2 className="text-2xl font-bold text-gray-900 mb-6">
|
||||
연락처 정보
|
||||
</h2>
|
||||
<div className="space-y-4">
|
||||
<div className="flex items-start space-x-3">
|
||||
<div className="w-6 h-6 bg-blue-100 rounded-full flex items-center justify-center flex-shrink-0 mt-1">
|
||||
<svg className="w-3 h-3 text-blue-600" fill="currentColor" viewBox="0 0 24 24">
|
||||
<path d="M12 2C8.13 2 5 5.13 5 9c0 5.25 7 13 7 13s7-7.75 7-13c0-3.87-3.13-7-7-7zm0 9.5c-1.38 0-2.5-1.12-2.5-2.5s1.12-2.5 2.5-2.5 2.5 1.12 2.5 2.5-1.12 2.5-2.5 2.5z"/>
|
||||
</svg>
|
||||
</div>
|
||||
<div>
|
||||
<div className="font-semibold text-gray-900">주소</div>
|
||||
<div className="text-gray-600">서울시 강남구 테헤란로 123</div>
|
||||
</div>
|
||||
</div>
|
||||
<div className="flex items-start space-x-3">
|
||||
<div className="w-6 h-6 bg-blue-100 rounded-full flex items-center justify-center flex-shrink-0 mt-1">
|
||||
<svg className="w-3 h-3 text-blue-600" fill="currentColor" viewBox="0 0 24 24">
|
||||
<path d="M20 4H4c-1.1 0-1.99.9-1.99 2L2 18c0 1.1.9 2 2 2h16c1.1 0 2-.9 2-2V6c0-1.1-.9-2-2-2zm0 4l-8 5-8-5V6l8 5 8-5v2z"/>
|
||||
</svg>
|
||||
</div>
|
||||
<div>
|
||||
<div className="font-semibold text-gray-900">이메일</div>
|
||||
<div className="text-gray-600">contact@nicepayments.com</div>
|
||||
</div>
|
||||
</div>
|
||||
<div className="flex items-start space-x-3">
|
||||
<div className="w-6 h-6 bg-blue-100 rounded-full flex items-center justify-center flex-shrink-0 mt-1">
|
||||
<svg className="w-3 h-3 text-blue-600" fill="currentColor" viewBox="0 0 24 24">
|
||||
<path d="M20.01 15.38c-1.23 0-2.42-.2-3.53-.56-.35-.12-.74-.03-1.01.24l-1.57 1.97c-2.83-1.35-5.48-3.9-6.89-6.83l1.95-1.66c.27-.28.35-.67.24-1.02-.37-1.11-.56-2.3-.56-3.53 0-.54-.45-.99-.99-.99H4.19C3.65 3 3 3.24 3 3.99 3 13.28 10.73 21 20.01 21c.71 0 .99-.63.99-1.18v-3.45c0-.54-.45-.99-.99-.99z"/>
|
||||
</svg>
|
||||
</div>
|
||||
<div>
|
||||
<div className="font-semibold text-gray-900">전화번호</div>
|
||||
<div className="text-gray-600">02-1234-5678</div>
|
||||
</div>
|
||||
</div>
|
||||
<div className="flex items-start space-x-3">
|
||||
<div className="w-6 h-6 bg-blue-100 rounded-full flex items-center justify-center flex-shrink-0 mt-1">
|
||||
<svg className="w-3 h-3 text-blue-600" fill="currentColor" viewBox="0 0 24 24">
|
||||
<path d="M12 2C6.48 2 2 6.48 2 12s4.48 10 10 10 10-4.48 10-10S17.52 2 12 2zm-2 15l-5-5 1.41-1.41L10 14.17l7.59-7.59L19 8l-9 9z"/>
|
||||
</svg>
|
||||
</div>
|
||||
<div>
|
||||
<div className="font-semibold text-gray-900">운영시간</div>
|
||||
<div className="text-gray-600">평일 09:00 - 18:00</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{/* Contact Form */}
|
||||
<div className="bg-white p-8 rounded-lg shadow-lg">
|
||||
<h2 className="text-2xl font-bold text-gray-900 mb-6">
|
||||
문의 양식
|
||||
</h2>
|
||||
<form onSubmit={handleSubmit} className="space-y-6">
|
||||
<div>
|
||||
<label htmlFor="name" className="block text-sm font-medium text-gray-700 mb-2">
|
||||
이름 *
|
||||
</label>
|
||||
<input
|
||||
type="text"
|
||||
id="name"
|
||||
name="name"
|
||||
value={formData.name}
|
||||
onChange={handleChange}
|
||||
className={`w-full px-3 py-2 border rounded-md focus:outline-none focus:ring-2 focus:ring-blue-500 ${
|
||||
errors.name ? 'border-red-500' : 'border-gray-300'
|
||||
}`}
|
||||
placeholder="이름을 입력하세요"
|
||||
/>
|
||||
{errors.name && <p className="mt-1 text-sm text-red-600">{errors.name}</p>}
|
||||
</div>
|
||||
|
||||
<div>
|
||||
<label htmlFor="email" className="block text-sm font-medium text-gray-700 mb-2">
|
||||
이메일 *
|
||||
</label>
|
||||
<input
|
||||
type="email"
|
||||
id="email"
|
||||
name="email"
|
||||
value={formData.email}
|
||||
onChange={handleChange}
|
||||
className={`w-full px-3 py-2 border rounded-md focus:outline-none focus:ring-2 focus:ring-blue-500 ${
|
||||
errors.email ? 'border-red-500' : 'border-gray-300'
|
||||
}`}
|
||||
placeholder="이메일을 입력하세요"
|
||||
/>
|
||||
{errors.email && <p className="mt-1 text-sm text-red-600">{errors.email}</p>}
|
||||
</div>
|
||||
|
||||
<div>
|
||||
<label htmlFor="phone" className="block text-sm font-medium text-gray-700 mb-2">
|
||||
전화번호 *
|
||||
</label>
|
||||
<input
|
||||
type="tel"
|
||||
id="phone"
|
||||
name="phone"
|
||||
value={formData.phone}
|
||||
onChange={handleChange}
|
||||
className={`w-full px-3 py-2 border rounded-md focus:outline-none focus:ring-2 focus:ring-blue-500 ${
|
||||
errors.phone ? 'border-red-500' : 'border-gray-300'
|
||||
}`}
|
||||
placeholder="전화번호를 입력하세요"
|
||||
/>
|
||||
{errors.phone && <p className="mt-1 text-sm text-red-600">{errors.phone}</p>}
|
||||
</div>
|
||||
|
||||
<div>
|
||||
<label htmlFor="subject" className="block text-sm font-medium text-gray-700 mb-2">
|
||||
문의 제목 *
|
||||
</label>
|
||||
<select
|
||||
id="subject"
|
||||
name="subject"
|
||||
value={formData.subject}
|
||||
onChange={handleChange}
|
||||
className={`w-full px-3 py-2 border rounded-md focus:outline-none focus:ring-2 focus:ring-blue-500 ${
|
||||
errors.subject ? 'border-red-500' : 'border-gray-300'
|
||||
}`}
|
||||
>
|
||||
<option value="">문의 유형을 선택하세요</option>
|
||||
<option value="service">서비스 문의</option>
|
||||
<option value="technical">기술 지원</option>
|
||||
<option value="billing">결제/요금 문의</option>
|
||||
<option value="partnership">제휴 문의</option>
|
||||
<option value="other">기타</option>
|
||||
</select>
|
||||
{errors.subject && <p className="mt-1 text-sm text-red-600">{errors.subject}</p>}
|
||||
</div>
|
||||
|
||||
<div>
|
||||
<label htmlFor="message" className="block text-sm font-medium text-gray-700 mb-2">
|
||||
문의 내용 *
|
||||
</label>
|
||||
<textarea
|
||||
id="message"
|
||||
name="message"
|
||||
value={formData.message}
|
||||
onChange={handleChange}
|
||||
rows={5}
|
||||
className={`w-full px-3 py-2 border rounded-md focus:outline-none focus:ring-2 focus:ring-blue-500 ${
|
||||
errors.message ? 'border-red-500' : 'border-gray-300'
|
||||
}`}
|
||||
placeholder="문의 내용을 자세히 입력하세요"
|
||||
/>
|
||||
{errors.message && <p className="mt-1 text-sm text-red-600">{errors.message}</p>}
|
||||
</div>
|
||||
|
||||
<button
|
||||
type="submit"
|
||||
disabled={isSubmitting}
|
||||
className="w-full bg-blue-600 text-white py-3 px-4 rounded-md hover:bg-blue-700 focus:outline-none focus:ring-2 focus:ring-blue-500 disabled:opacity-50 disabled:cursor-not-allowed transition-colors"
|
||||
>
|
||||
{isSubmitting ? '전송 중...' : '문의 보내기'}
|
||||
</button>
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
export default Contact;
|
||||
Reference in New Issue
Block a user