feat: 토정비결 및 PDF 저장 기능 추가

- 토정비결 페이지 구현 (연간/월별 운세)
- 분야별 운세 (재물, 건강, 관운, 애정)
- PDF 저장 기능 구현 (jsPDF + html2canvas)
- 모든 결과 페이지에 PDF 다운로드 기능 추가
- PDFButton 재사용 가능한 컴포넌트 생성
- 홈페이지에 토정비결 링크 추가
- 페이지 간 네비게이션 링크 업데이트

Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
This commit is contained in:
2026-02-11 23:44:55 +09:00
parent 08d6f71fd1
commit f85e857bea
11 changed files with 950 additions and 17 deletions

View File

@@ -0,0 +1,139 @@
'use client';
import { useState } from 'react';
import { useRouter } from 'next/navigation';
export default function TojeongForm() {
const router = useRouter();
const [year, setYear] = useState('');
const [month, setMonth] = useState('');
const [day, setDay] = useState('');
const [gender, setGender] = useState<'male' | 'female'>('male');
const [targetYear, setTargetYear] = useState(new Date().getFullYear().toString());
const handleSubmit = (e: React.FormEvent) => {
e.preventDefault();
if (!year || !month || !day) {
alert('생년월일을 모두 입력해주세요.');
return;
}
// URL 파라미터로 전달
const params = new URLSearchParams({
year,
month,
day,
gender,
targetYear
});
router.push(`/tojeong/result?${params.toString()}`);
};
return (
<form onSubmit={handleSubmit} className="bg-white rounded-3xl shadow-2xl p-8 md:p-12">
<h2 className="text-2xl font-bold text-gray-900 mb-8 text-center"> </h2>
<div className="space-y-6">
{/* 생년월일 */}
<div>
<label className="block text-left text-sm font-semibold text-gray-700 mb-2">
</label>
<div className="grid grid-cols-3 gap-3">
<input
type="number"
placeholder="년 (예: 1990)"
className="px-4 py-3 border-2 border-gray-200 rounded-xl focus:border-amber-500 focus:outline-none transition"
min="1900"
max="2100"
value={year}
onChange={(e) => setYear(e.target.value)}
required
/>
<input
type="number"
placeholder="월 (1-12)"
className="px-4 py-3 border-2 border-gray-200 rounded-xl focus:border-amber-500 focus:outline-none transition"
min="1"
max="12"
value={month}
onChange={(e) => setMonth(e.target.value)}
required
/>
<input
type="number"
placeholder="일 (1-31)"
className="px-4 py-3 border-2 border-gray-200 rounded-xl focus:border-amber-500 focus:outline-none transition"
min="1"
max="31"
value={day}
onChange={(e) => setDay(e.target.value)}
required
/>
</div>
</div>
{/* 성별 선택 */}
<div>
<label className="block text-left text-sm font-semibold text-gray-700 mb-2">
</label>
<div className="grid grid-cols-2 gap-3">
<button
type="button"
onClick={() => setGender('male')}
className={`px-6 py-3 rounded-xl font-semibold transition ${
gender === 'male'
? 'bg-amber-600 text-white'
: 'bg-white border-2 border-gray-200 text-gray-700 hover:border-amber-500 hover:text-amber-600'
}`}
>
</button>
<button
type="button"
onClick={() => setGender('female')}
className={`px-6 py-3 rounded-xl font-semibold transition ${
gender === 'female'
? 'bg-amber-600 text-white'
: 'bg-white border-2 border-gray-200 text-gray-700 hover:border-amber-500 hover:text-amber-600'
}`}
>
</button>
</div>
</div>
{/* 확인할 년도 */}
<div>
<label className="block text-left text-sm font-semibold text-gray-700 mb-2">
</label>
<select
className="w-full px-4 py-3 border-2 border-gray-200 rounded-xl focus:border-amber-500 focus:outline-none transition"
value={targetYear}
onChange={(e) => setTargetYear(e.target.value)}
>
<option value={new Date().getFullYear() - 1}>{new Date().getFullYear() - 1}</option>
<option value={new Date().getFullYear()}>{new Date().getFullYear()} ()</option>
<option value={new Date().getFullYear() + 1}>{new Date().getFullYear() + 1} ()</option>
</select>
</div>
</div>
{/* 제출 버튼 */}
<button
type="submit"
className="w-full mt-8 bg-gradient-to-r from-amber-600 to-orange-600 text-white py-4 rounded-xl text-lg font-bold hover:from-amber-700 hover:to-orange-700 transition shadow-lg hover:shadow-xl"
>
🎋
</button>
<p className="text-sm text-gray-500 text-center mt-4">
* .
</p>
</form>
);
}