forked from UKSOURCE/hailearning.edu.vn
feat: setup frontend Next.js project structure
This commit is contained in:
3
.gitignore
vendored
3
.gitignore
vendored
@@ -39,3 +39,6 @@ yarn-error.log*
|
|||||||
# typescript
|
# typescript
|
||||||
*.tsbuildinfo
|
*.tsbuildinfo
|
||||||
next-env.d.ts
|
next-env.d.ts
|
||||||
|
|
||||||
|
#vs code
|
||||||
|
/.vscode
|
||||||
0
Dockerfile
Normal file
0
Dockerfile
Normal file
218
README.md
218
README.md
@@ -1,36 +1,218 @@
|
|||||||
This is a [Next.js](https://nextjs.org) project bootstrapped with [`create-next-app`](https://nextjs.org/docs/app/api-reference/cli/create-next-app).
|
# VisaService - Website Dịch Vụ Visa
|
||||||
|
|
||||||
## Getting Started
|
Website dịch vụ visa chuyên nghiệp được xây dựng với Next.js 15, TypeScript và Tailwind CSS. Hỗ trợ tư vấn visa cho hơn 50 quốc gia với tỷ lệ thành công 99%.
|
||||||
|
|
||||||
First, run the development server:
|
## 🚀 Tính Năng
|
||||||
|
|
||||||
|
- **8 Trang Chính**: Trang chủ, Giới thiệu, Dịch vụ, Visa, Quốc gia, Bảng giá, Blog, Liên hệ
|
||||||
|
- **Hệ Thống Đặt Lịch**: Đặt lịch tư vấn với chuyên gia
|
||||||
|
- **Responsive Design**: Tối ưu cho mọi thiết bị
|
||||||
|
- **JSON-Based Content**: Dễ dàng quản lý nội dung
|
||||||
|
- **Interactive Components**: Form, accordion, filter, modal
|
||||||
|
- **SEO Optimized**: Metadata và structure tối ưu
|
||||||
|
|
||||||
|
## 🛠️ Công Nghệ Sử Dụng
|
||||||
|
|
||||||
|
- **Framework**: Next.js 15 (App Router)
|
||||||
|
- **Language**: TypeScript
|
||||||
|
- **Styling**: Tailwind CSS
|
||||||
|
- **Icons**: Emoji icons
|
||||||
|
- **Deployment**: Docker support
|
||||||
|
|
||||||
|
## 📁 Cấu Trúc Thư Mục
|
||||||
|
|
||||||
|
```
|
||||||
|
app/
|
||||||
|
├── components/ # Shared components
|
||||||
|
│ ├── Header.tsx # Navigation header
|
||||||
|
│ └── Footer.tsx # Site footer
|
||||||
|
├── about/ # Trang giới thiệu
|
||||||
|
│ ├── about.json # Dữ liệu về công ty
|
||||||
|
│ └── page.tsx # Component trang
|
||||||
|
├── services/ # Trang dịch vụ
|
||||||
|
│ ├── services.json # Dữ liệu dịch vụ
|
||||||
|
│ └── page.tsx # Component trang
|
||||||
|
├── visa/ # Trang visa
|
||||||
|
│ ├── visa.json # Dữ liệu các loại visa
|
||||||
|
│ └── page.tsx # Component trang
|
||||||
|
├── countries/ # Trang quốc gia
|
||||||
|
│ ├── countries.json # Dữ liệu quốc gia
|
||||||
|
│ └── page.tsx # Component trang
|
||||||
|
├── pricing/ # Trang bảng giá
|
||||||
|
│ ├── pricing.json # Dữ liệu giá cả
|
||||||
|
│ └── page.tsx # Component trang
|
||||||
|
├── blog/ # Trang blog
|
||||||
|
│ ├── blog.json # Dữ liệu bài viết
|
||||||
|
│ └── page.tsx # Component trang
|
||||||
|
├── contact/ # Trang liên hệ
|
||||||
|
│ ├── contact.json # Thông tin liên hệ
|
||||||
|
│ └── page.tsx # Component trang
|
||||||
|
├── appointment/ # Trang đặt lịch
|
||||||
|
│ ├── appointment.json # Dữ liệu booking
|
||||||
|
│ └── page.tsx # Component trang
|
||||||
|
├── layout.tsx # Root layout
|
||||||
|
├── page.tsx # Trang chủ
|
||||||
|
└── globals.css # Global styles
|
||||||
|
```
|
||||||
|
|
||||||
|
## 🚀 Hướng Dẫn Chạy Dự Án
|
||||||
|
|
||||||
|
### Yêu Cầu Hệ Thống
|
||||||
|
|
||||||
|
- Node.js 18+
|
||||||
|
- npm, yarn, hoặc pnpm
|
||||||
|
|
||||||
|
### Cài Đặt và Chạy
|
||||||
|
|
||||||
|
1. **Clone repository**
|
||||||
|
|
||||||
|
```bash
|
||||||
|
git clone <repository-url>
|
||||||
|
cd visa-service-website
|
||||||
|
```
|
||||||
|
|
||||||
|
2. **Cài đặt dependencies**
|
||||||
|
|
||||||
|
```bash
|
||||||
|
npm install
|
||||||
|
# hoặc
|
||||||
|
yarn install
|
||||||
|
# hoặc
|
||||||
|
pnpm install
|
||||||
|
```
|
||||||
|
|
||||||
|
3. **Chạy development server**
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
npm run dev
|
npm run dev
|
||||||
# or
|
# hoặc
|
||||||
yarn dev
|
yarn dev
|
||||||
# or
|
# hoặc
|
||||||
pnpm dev
|
pnpm dev
|
||||||
# or
|
|
||||||
bun dev
|
|
||||||
```
|
```
|
||||||
|
|
||||||
Open [http://localhost:3000](http://localhost:3000) with your browser to see the result.
|
4. **Mở trình duyệt**
|
||||||
|
Truy cập [http://localhost:3000](http://localhost:3000) để xem website
|
||||||
|
|
||||||
You can start editing the page by modifying `app/page.tsx`. The page auto-updates as you edit the file.
|
### Scripts Có Sẵn
|
||||||
|
|
||||||
This project uses [`next/font`](https://nextjs.org/docs/app/building-your-application/optimizing/fonts) to automatically optimize and load [Geist](https://vercel.com/font), a new font family for Vercel.
|
```bash
|
||||||
|
npm run dev # Chạy development server
|
||||||
|
npm run build # Build production
|
||||||
|
npm run start # Chạy production server
|
||||||
|
npm run lint # Kiểm tra code style
|
||||||
|
```
|
||||||
|
|
||||||
## Learn More
|
## 🐳 Chạy Với Docker
|
||||||
|
|
||||||
To learn more about Next.js, take a look at the following resources:
|
### Development với Docker
|
||||||
|
|
||||||
- [Next.js Documentation](https://nextjs.org/docs) - learn about Next.js features and API.
|
```bash
|
||||||
- [Learn Next.js](https://nextjs.org/learn) - an interactive Next.js tutorial.
|
# Build và chạy container
|
||||||
|
docker-compose up --build
|
||||||
|
|
||||||
You can check out [the Next.js GitHub repository](https://github.com/vercel/next.js) - your feedback and contributions are welcome!
|
# Chạy ở background
|
||||||
|
docker-compose up -d
|
||||||
|
|
||||||
## Deploy on Vercel
|
# Dừng container
|
||||||
|
docker-compose down
|
||||||
|
```
|
||||||
|
|
||||||
The easiest way to deploy your Next.js app is to use the [Vercel Platform](https://vercel.com/new?utm_medium=default-template&filter=next.js&utm_source=create-next-app&utm_campaign=create-next-app-readme) from the creators of Next.js.
|
### Production với Docker
|
||||||
|
|
||||||
Check out our [Next.js deployment documentation](https://nextjs.org/docs/app/building-your-application/deploying) for more details.
|
```bash
|
||||||
|
# Build production image
|
||||||
|
docker build -t visa-service .
|
||||||
|
|
||||||
|
# Chạy container
|
||||||
|
docker run -p 3000:3000 visa-service
|
||||||
|
```
|
||||||
|
|
||||||
|
## 📝 Tùy Chỉnh Nội Dung
|
||||||
|
|
||||||
|
### Cập Nhật Dữ Liệu
|
||||||
|
|
||||||
|
Mỗi trang có file JSON riêng chứa dữ liệu:
|
||||||
|
|
||||||
|
- `about/about.json` - Thông tin công ty
|
||||||
|
- `services/services.json` - Danh sách dịch vụ
|
||||||
|
- `visa/visa.json` - Các loại visa
|
||||||
|
- `countries/countries.json` - Quốc gia hỗ trợ
|
||||||
|
- `pricing/pricing.json` - Bảng giá dịch vụ
|
||||||
|
- `blog/blog.json` - Bài viết blog
|
||||||
|
- `contact/contact.json` - Thông tin liên hệ
|
||||||
|
- `appointment/appointment.json` - Cấu hình đặt lịch
|
||||||
|
|
||||||
|
### Thêm Trang Mới
|
||||||
|
|
||||||
|
1. Tạo thư mục mới trong `app/`
|
||||||
|
2. Tạo file `data.json` chứa dữ liệu
|
||||||
|
3. Tạo file `page.tsx` với React component
|
||||||
|
4. Cập nhật navigation trong `Header.tsx`
|
||||||
|
|
||||||
|
### Tùy Chỉnh Styling
|
||||||
|
|
||||||
|
- Sử dụng Tailwind CSS classes
|
||||||
|
- Tùy chỉnh theme trong `tailwind.config.js`
|
||||||
|
- Global styles trong `app/globals.css`
|
||||||
|
|
||||||
|
## 🌐 Deployment
|
||||||
|
|
||||||
|
### Vercel (Khuyến nghị)
|
||||||
|
|
||||||
|
1. Push code lên GitHub
|
||||||
|
2. Kết nối repository với Vercel
|
||||||
|
3. Deploy tự động
|
||||||
|
|
||||||
|
### Netlify
|
||||||
|
|
||||||
|
1. Build project: `npm run build`
|
||||||
|
2. Upload thư mục `out/` lên Netlify
|
||||||
|
|
||||||
|
### VPS/Server
|
||||||
|
|
||||||
|
1. Build project: `npm run build`
|
||||||
|
2. Chạy: `npm run start`
|
||||||
|
3. Sử dụng PM2 hoặc Docker cho production
|
||||||
|
|
||||||
|
## 📱 Responsive Design
|
||||||
|
|
||||||
|
Website được tối ưu cho:
|
||||||
|
|
||||||
|
- **Mobile**: < 768px
|
||||||
|
- **Tablet**: 768px - 1024px
|
||||||
|
- **Desktop**: > 1024px
|
||||||
|
|
||||||
|
## 🔧 Cấu Hình Môi Trường
|
||||||
|
|
||||||
|
Tạo file `.env.local` (tùy chọn):
|
||||||
|
|
||||||
|
```env
|
||||||
|
NEXT_PUBLIC_SITE_URL=http://localhost:3000
|
||||||
|
NEXT_PUBLIC_CONTACT_EMAIL=info@visaservice.com
|
||||||
|
NEXT_PUBLIC_PHONE=1900-1234
|
||||||
|
```
|
||||||
|
|
||||||
|
## 🤝 Đóng Góp
|
||||||
|
|
||||||
|
1. Fork repository
|
||||||
|
2. Tạo feature branch: `git checkout -b feature/new-feature`
|
||||||
|
3. Commit changes: `git commit -m 'Add new feature'`
|
||||||
|
4. Push branch: `git push origin feature/new-feature`
|
||||||
|
5. Tạo Pull Request
|
||||||
|
|
||||||
|
## 📄 License
|
||||||
|
|
||||||
|
MIT License - xem file [LICENSE](LICENSE) để biết thêm chi tiết.
|
||||||
|
|
||||||
|
## 📞 Hỗ Trợ
|
||||||
|
|
||||||
|
- **Email**: info@visaservice.com
|
||||||
|
- **Phone**: 1900 1234
|
||||||
|
- **Website**: [visaservice.com](https://visaservice.com)
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
**Phát triển bởi**: VisaService Team
|
||||||
|
**Phiên bản**: 1.0.0
|
||||||
|
**Cập nhật**: January 2025
|
||||||
|
|||||||
33
app/about/about.json
Normal file
33
app/about/about.json
Normal file
@@ -0,0 +1,33 @@
|
|||||||
|
{
|
||||||
|
"title": "Về Chúng Tôi",
|
||||||
|
"subtitle": "Đối tác tin cậy cho dịch vụ visa và du lịch",
|
||||||
|
"description": "Chúng tôi là công ty hàng đầu trong lĩnh vực tư vấn visa và dịch vụ du lịch, với hơn 10 năm kinh nghiệm phục vụ khách hàng.",
|
||||||
|
"sections": [
|
||||||
|
{
|
||||||
|
"heading": "Sứ Mệnh",
|
||||||
|
"content": "Mang đến cho khách hàng những trải nghiệm du lịch tuyệt vời và hỗ trợ thủ tục visa một cách nhanh chóng, chính xác."
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"heading": "Tầm Nhìn",
|
||||||
|
"content": "Trở thành công ty dẫn đầu trong lĩnh vực dịch vụ visa và du lịch tại Việt Nam."
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"heading": "Giá Trị Cốt Lõi",
|
||||||
|
"content": "Uy tín - Chất lượng - Tận tâm - Chuyên nghiệp"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"stats": [
|
||||||
|
{
|
||||||
|
"number": "10000+",
|
||||||
|
"label": "Khách hàng hài lòng"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"number": "50+",
|
||||||
|
"label": "Quốc gia hỗ trợ visa"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"number": "99%",
|
||||||
|
"label": "Tỷ lệ thành công"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
49
app/about/page.tsx
Normal file
49
app/about/page.tsx
Normal file
@@ -0,0 +1,49 @@
|
|||||||
|
import aboutData from "./about.json";
|
||||||
|
|
||||||
|
export default function AboutPage() {
|
||||||
|
return (
|
||||||
|
<div className="container mx-auto px-4 py-8">
|
||||||
|
<div className="max-w-4xl mx-auto">
|
||||||
|
{/* Header */}
|
||||||
|
<div className="text-center mb-12">
|
||||||
|
<h1 className="text-4xl font-bold text-gray-900 mb-4">
|
||||||
|
{aboutData.title}
|
||||||
|
</h1>
|
||||||
|
<p className="text-xl text-gray-600 mb-6">{aboutData.subtitle}</p>
|
||||||
|
<p className="text-lg text-gray-700 leading-relaxed">
|
||||||
|
{aboutData.description}
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
{/* Sections */}
|
||||||
|
<div className="grid md:grid-cols-3 gap-8 mb-12">
|
||||||
|
{aboutData.sections.map((section, index) => (
|
||||||
|
<div key={index} className="bg-white p-6 rounded-lg shadow-md">
|
||||||
|
<h3 className="text-xl font-semibold text-gray-900 mb-4">
|
||||||
|
{section.heading}
|
||||||
|
</h3>
|
||||||
|
<p className="text-gray-700 leading-relaxed">{section.content}</p>
|
||||||
|
</div>
|
||||||
|
))}
|
||||||
|
</div>
|
||||||
|
|
||||||
|
{/* Stats */}
|
||||||
|
<div className="bg-blue-50 rounded-lg p-8">
|
||||||
|
<h2 className="text-2xl font-bold text-center text-gray-900 mb-8">
|
||||||
|
Thành Tích Của Chúng Tôi
|
||||||
|
</h2>
|
||||||
|
<div className="grid md:grid-cols-3 gap-8">
|
||||||
|
{aboutData.stats.map((stat, index) => (
|
||||||
|
<div key={index} className="text-center">
|
||||||
|
<div className="text-3xl font-bold text-blue-600 mb-2">
|
||||||
|
{stat.number}
|
||||||
|
</div>
|
||||||
|
<div className="text-gray-700">{stat.label}</div>
|
||||||
|
</div>
|
||||||
|
))}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
}
|
||||||
255
app/appointment/appointment.json
Normal file
255
app/appointment/appointment.json
Normal file
@@ -0,0 +1,255 @@
|
|||||||
|
{
|
||||||
|
"title": "Đặt Lịch Tư Vấn",
|
||||||
|
"subtitle": "Nhận tư vấn miễn phí từ chuyên gia visa của chúng tôi",
|
||||||
|
"hero": {
|
||||||
|
"title": "Tư Vấn Visa Miễn Phí",
|
||||||
|
"description": "Đặt lịch hẹn với chuyên gia để được tư vấn chi tiết về visa và dịch vụ phù hợp nhất cho bạn.",
|
||||||
|
"benefits": [
|
||||||
|
"Tư vấn 1-1 với chuyên gia",
|
||||||
|
"Phân tích hồ sơ miễn phí",
|
||||||
|
"Báo giá chi tiết",
|
||||||
|
"Lộ trình xin visa rõ ràng"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"appointmentTypes": [
|
||||||
|
{
|
||||||
|
"id": "consultation",
|
||||||
|
"name": "Tư Vấn Tổng Quát",
|
||||||
|
"duration": "30 phút",
|
||||||
|
"price": "Miễn phí",
|
||||||
|
"description": "Tư vấn về loại visa phù hợp, yêu cầu cơ bản và quy trình xin visa",
|
||||||
|
"icon": "💬",
|
||||||
|
"popular": true
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "document-review",
|
||||||
|
"name": "Kiểm Tra Hồ Sơ",
|
||||||
|
"duration": "45 phút",
|
||||||
|
"price": "500,000 VNĐ",
|
||||||
|
"description": "Kiểm tra chi tiết hồ sơ hiện tại và đưa ra lời khuyên cải thiện",
|
||||||
|
"icon": "📋",
|
||||||
|
"popular": false
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "interview-prep",
|
||||||
|
"name": "Luyện Phỏng Vấn",
|
||||||
|
"duration": "60 phút",
|
||||||
|
"price": "800,000 VNĐ",
|
||||||
|
"description": "Luyện tập kỹ năng phỏng vấn visa với chuyên gia có kinh nghiệm",
|
||||||
|
"icon": "🎯",
|
||||||
|
"popular": false
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "premium",
|
||||||
|
"name": "Tư Vấn Cao Cấp",
|
||||||
|
"duration": "90 phút",
|
||||||
|
"price": "1,200,000 VNĐ",
|
||||||
|
"description": "Tư vấn toàn diện bao gồm chiến lược, hồ sơ và luyện phỏng vấn",
|
||||||
|
"icon": "⭐",
|
||||||
|
"popular": false
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"timeSlots": [
|
||||||
|
{
|
||||||
|
"time": "08:00",
|
||||||
|
"label": "8:00 AM",
|
||||||
|
"available": true
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"time": "09:00",
|
||||||
|
"label": "9:00 AM",
|
||||||
|
"available": true
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"time": "10:00",
|
||||||
|
"label": "10:00 AM",
|
||||||
|
"available": false
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"time": "11:00",
|
||||||
|
"label": "11:00 AM",
|
||||||
|
"available": true
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"time": "13:00",
|
||||||
|
"label": "1:00 PM",
|
||||||
|
"available": true
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"time": "14:00",
|
||||||
|
"label": "2:00 PM",
|
||||||
|
"available": true
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"time": "15:00",
|
||||||
|
"label": "3:00 PM",
|
||||||
|
"available": false
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"time": "16:00",
|
||||||
|
"label": "4:00 PM",
|
||||||
|
"available": true
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"time": "17:00",
|
||||||
|
"label": "5:00 PM",
|
||||||
|
"available": true
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"consultants": [
|
||||||
|
{
|
||||||
|
"id": "nguyen-van-a",
|
||||||
|
"name": "Nguyễn Văn A",
|
||||||
|
"title": "Chuyên gia Visa Mỹ & Canada",
|
||||||
|
"experience": "8 năm kinh nghiệm",
|
||||||
|
"specialties": ["Visa Mỹ", "Visa Canada", "Visa du học"],
|
||||||
|
"languages": ["Tiếng Việt", "English"],
|
||||||
|
"rating": 4.9,
|
||||||
|
"reviews": 156,
|
||||||
|
"avatar": "/images/consultant-1.jpg",
|
||||||
|
"description": "Chuyên gia hàng đầu về visa Bắc Mỹ với tỷ lệ thành công 98%"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "tran-thi-b",
|
||||||
|
"name": "Trần Thị B",
|
||||||
|
"title": "Chuyên gia Visa Châu Âu",
|
||||||
|
"experience": "6 năm kinh nghiệm",
|
||||||
|
"specialties": ["Visa Schengen", "Visa Anh", "Visa du lịch"],
|
||||||
|
"languages": ["Tiếng Việt", "English", "Français"],
|
||||||
|
"rating": 4.8,
|
||||||
|
"reviews": 203,
|
||||||
|
"avatar": "/images/consultant-2.jpg",
|
||||||
|
"description": "Chuyên gia visa châu Âu với kinh nghiệm sống và làm việc tại Pháp"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "le-van-c",
|
||||||
|
"name": "Lê Văn C",
|
||||||
|
"title": "Chuyên gia Visa Châu Á",
|
||||||
|
"experience": "5 năm kinh nghiệm",
|
||||||
|
"specialties": ["Visa Nhật Bản", "Visa Hàn Quốc", "Visa Singapore"],
|
||||||
|
"languages": ["Tiếng Việt", "日本語", "한국어"],
|
||||||
|
"rating": 4.9,
|
||||||
|
"reviews": 134,
|
||||||
|
"avatar": "/images/consultant-3.jpg",
|
||||||
|
"description": "Chuyên gia visa châu Á với khả năng giao tiếp đa ngôn ngữ"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"formFields": [
|
||||||
|
{
|
||||||
|
"name": "fullName",
|
||||||
|
"label": "Họ và tên",
|
||||||
|
"type": "text",
|
||||||
|
"required": true,
|
||||||
|
"placeholder": "Nhập họ và tên đầy đủ"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "email",
|
||||||
|
"label": "Email",
|
||||||
|
"type": "email",
|
||||||
|
"required": true,
|
||||||
|
"placeholder": "example@email.com"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "phone",
|
||||||
|
"label": "Số điện thoại",
|
||||||
|
"type": "tel",
|
||||||
|
"required": true,
|
||||||
|
"placeholder": "+84 xxx xxx xxx"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "visaType",
|
||||||
|
"label": "Loại visa quan tâm",
|
||||||
|
"type": "select",
|
||||||
|
"required": true,
|
||||||
|
"options": [
|
||||||
|
"Visa du lịch",
|
||||||
|
"Visa công tác",
|
||||||
|
"Visa du học",
|
||||||
|
"Visa thăm thân",
|
||||||
|
"Visa lao động",
|
||||||
|
"Chưa xác định"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "country",
|
||||||
|
"label": "Quốc gia đích",
|
||||||
|
"type": "select",
|
||||||
|
"required": true,
|
||||||
|
"options": [
|
||||||
|
"Mỹ",
|
||||||
|
"Canada",
|
||||||
|
"Schengen",
|
||||||
|
"Anh",
|
||||||
|
"Úc",
|
||||||
|
"Nhật Bản",
|
||||||
|
"Hàn Quốc",
|
||||||
|
"Singapore",
|
||||||
|
"Khác"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "travelDate",
|
||||||
|
"label": "Dự kiến ngày đi",
|
||||||
|
"type": "date",
|
||||||
|
"required": false,
|
||||||
|
"placeholder": ""
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "previousVisa",
|
||||||
|
"label": "Đã từng xin visa nước này?",
|
||||||
|
"type": "select",
|
||||||
|
"required": false,
|
||||||
|
"options": [
|
||||||
|
"Chưa bao giờ",
|
||||||
|
"Đã xin và được duyệt",
|
||||||
|
"Đã xin nhưng bị từ chối",
|
||||||
|
"Đang trong quá trình xin"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "notes",
|
||||||
|
"label": "Ghi chú thêm",
|
||||||
|
"type": "textarea",
|
||||||
|
"required": false,
|
||||||
|
"placeholder": "Mô tả thêm về tình huống của bạn..."
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"faqs": [
|
||||||
|
{
|
||||||
|
"question": "Tư vấn có thực sự miễn phí không?",
|
||||||
|
"answer": "Có, buổi tư vấn tổng quát 30 phút đầu tiên hoàn toàn miễn phí. Bạn chỉ cần đặt lịch và tham gia."
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"question": "Tôi có thể hủy hoặc đổi lịch hẹn không?",
|
||||||
|
"answer": "Có, bạn có thể hủy hoặc đổi lịch hẹn trước 24 giờ mà không mất phí. Liên hệ với chúng tôi để thay đổi."
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"question": "Buổi tư vấn diễn ra như thế nào?",
|
||||||
|
"answer": "Buổi tư vấn có thể diễn ra trực tiếp tại văn phòng hoặc online qua video call tùy theo lựa chọn của bạn."
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"question": "Tôi cần chuẩn bị gì cho buổi tư vấn?",
|
||||||
|
"answer": "Bạn nên chuẩn bị các câu hỏi cụ thể và nếu có, mang theo hộ chiếu và các giấy tờ liên quan để được tư vấn chính xác hơn."
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"offices": [
|
||||||
|
{
|
||||||
|
"name": "TP. Hồ Chí Minh",
|
||||||
|
"address": "123 Nguyễn Huệ, Quận 1",
|
||||||
|
"phone": "+84 28 1234 5678",
|
||||||
|
"hours": "8:00 - 18:00 (T2-T6), 8:00 - 12:00 (T7)"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "Hà Nội",
|
||||||
|
"address": "456 Hoàn Kiếm, Hà Nội",
|
||||||
|
"phone": "+84 24 1234 5678",
|
||||||
|
"hours": "8:00 - 18:00 (T2-T6), 8:00 - 12:00 (T7)"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "Đà Nẵng",
|
||||||
|
"address": "789 Hải Châu, Đà Nẵng",
|
||||||
|
"phone": "+84 236 1234 567",
|
||||||
|
"hours": "8:00 - 18:00 (T2-T6), 8:00 - 12:00 (T7)"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
501
app/appointment/page.tsx
Normal file
501
app/appointment/page.tsx
Normal file
@@ -0,0 +1,501 @@
|
|||||||
|
"use client";
|
||||||
|
|
||||||
|
import { useState } from "react";
|
||||||
|
import appointmentData from "./appointment.json";
|
||||||
|
|
||||||
|
export default function AppointmentPage() {
|
||||||
|
const [selectedType, setSelectedType] = useState("consultation");
|
||||||
|
const [selectedConsultant, setSelectedConsultant] = useState("");
|
||||||
|
const [selectedDate, setSelectedDate] = useState("");
|
||||||
|
const [selectedTime, setSelectedTime] = useState("");
|
||||||
|
const [selectedOffice, setSelectedOffice] = useState("online");
|
||||||
|
const [openFaq, setOpenFaq] = useState<number | null>(null);
|
||||||
|
|
||||||
|
const [formData, setFormData] = useState({
|
||||||
|
fullName: "",
|
||||||
|
email: "",
|
||||||
|
phone: "",
|
||||||
|
visaType: "",
|
||||||
|
country: "",
|
||||||
|
travelDate: "",
|
||||||
|
previousVisa: "",
|
||||||
|
notes: "",
|
||||||
|
});
|
||||||
|
|
||||||
|
const handleInputChange = (
|
||||||
|
e: React.ChangeEvent<
|
||||||
|
HTMLInputElement | HTMLTextAreaElement | HTMLSelectElement
|
||||||
|
>,
|
||||||
|
) => {
|
||||||
|
const { name, value } = e.target;
|
||||||
|
setFormData((prev) => ({
|
||||||
|
...prev,
|
||||||
|
[name]: value,
|
||||||
|
}));
|
||||||
|
};
|
||||||
|
|
||||||
|
const handleSubmit = (e: React.FormEvent) => {
|
||||||
|
e.preventDefault();
|
||||||
|
console.log("Appointment booked:", {
|
||||||
|
type: selectedType,
|
||||||
|
consultant: selectedConsultant,
|
||||||
|
date: selectedDate,
|
||||||
|
time: selectedTime,
|
||||||
|
office: selectedOffice,
|
||||||
|
...formData,
|
||||||
|
});
|
||||||
|
alert(
|
||||||
|
"Đặt lịch thành công! Chúng tôi sẽ liên hệ xác nhận trong thời gian sớm nhất.",
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
const toggleFaq = (index: number) => {
|
||||||
|
setOpenFaq(openFaq === index ? null : index);
|
||||||
|
};
|
||||||
|
|
||||||
|
const selectedAppointmentType = appointmentData.appointmentTypes.find(
|
||||||
|
(type) => type.id === selectedType,
|
||||||
|
);
|
||||||
|
const selectedConsultantData = appointmentData.consultants.find(
|
||||||
|
(c) => c.id === selectedConsultant,
|
||||||
|
);
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div className="container mx-auto px-4 py-8">
|
||||||
|
<div className="max-w-6xl mx-auto">
|
||||||
|
{/* Header */}
|
||||||
|
<div className="text-center mb-12">
|
||||||
|
<h1 className="text-4xl font-bold text-gray-900 mb-4">
|
||||||
|
{appointmentData.title}
|
||||||
|
</h1>
|
||||||
|
<p className="text-xl text-gray-600 mb-8">
|
||||||
|
{appointmentData.subtitle}
|
||||||
|
</p>
|
||||||
|
|
||||||
|
{/* Benefits */}
|
||||||
|
<div className="grid md:grid-cols-4 gap-6 max-w-4xl mx-auto">
|
||||||
|
{appointmentData.hero.benefits.map((benefit, index) => (
|
||||||
|
<div key={index} className="bg-blue-50 p-4 rounded-lg">
|
||||||
|
<p className="text-blue-800 font-medium">{benefit}</p>
|
||||||
|
</div>
|
||||||
|
))}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div className="grid lg:grid-cols-3 gap-8">
|
||||||
|
{/* Booking Form */}
|
||||||
|
<div className="lg:col-span-2">
|
||||||
|
<div className="bg-white rounded-lg shadow-lg p-6">
|
||||||
|
<h2 className="text-2xl font-bold text-gray-900 mb-6">
|
||||||
|
Đặt Lịch Hẹn
|
||||||
|
</h2>
|
||||||
|
|
||||||
|
<form onSubmit={handleSubmit} className="space-y-6">
|
||||||
|
{/* Appointment Type */}
|
||||||
|
<div>
|
||||||
|
<label className="block text-sm font-medium text-gray-700 mb-3">
|
||||||
|
Loại tư vấn <span className="text-red-500">*</span>
|
||||||
|
</label>
|
||||||
|
<div className="grid md:grid-cols-2 gap-4">
|
||||||
|
{appointmentData.appointmentTypes.map((type) => (
|
||||||
|
<div
|
||||||
|
key={type.id}
|
||||||
|
className={`border rounded-lg p-4 cursor-pointer transition-colors ${
|
||||||
|
selectedType === type.id
|
||||||
|
? "border-blue-500 bg-blue-50"
|
||||||
|
: "border-gray-200 hover:border-gray-300"
|
||||||
|
}`}
|
||||||
|
onClick={() => setSelectedType(type.id)}
|
||||||
|
>
|
||||||
|
<div className="flex items-center justify-between mb-2">
|
||||||
|
<div className="flex items-center">
|
||||||
|
<span className="text-2xl mr-3">{type.icon}</span>
|
||||||
|
<div>
|
||||||
|
<h3 className="font-semibold">{type.name}</h3>
|
||||||
|
{type.popular && (
|
||||||
|
<span className="bg-green-100 text-green-800 px-2 py-1 rounded text-xs">
|
||||||
|
Phổ biến
|
||||||
|
</span>
|
||||||
|
)}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div className="text-right">
|
||||||
|
<div className="font-bold text-blue-600">
|
||||||
|
{type.price}
|
||||||
|
</div>
|
||||||
|
<div className="text-sm text-gray-500">
|
||||||
|
{type.duration}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<p className="text-gray-600 text-sm">
|
||||||
|
{type.description}
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
|
))}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
{/* Consultant Selection */}
|
||||||
|
<div>
|
||||||
|
<label className="block text-sm font-medium text-gray-700 mb-3">
|
||||||
|
Chọn chuyên gia
|
||||||
|
</label>
|
||||||
|
<div className="space-y-3">
|
||||||
|
<div
|
||||||
|
className={`border rounded-lg p-4 cursor-pointer transition-colors ${
|
||||||
|
selectedConsultant === ""
|
||||||
|
? "border-blue-500 bg-blue-50"
|
||||||
|
: "border-gray-200 hover:border-gray-300"
|
||||||
|
}`}
|
||||||
|
onClick={() => setSelectedConsultant("")}
|
||||||
|
>
|
||||||
|
<div className="font-medium">
|
||||||
|
Để chúng tôi chọn chuyên gia phù hợp
|
||||||
|
</div>
|
||||||
|
<div className="text-sm text-gray-600">
|
||||||
|
Dựa trên loại visa và quốc gia bạn quan tâm
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
{appointmentData.consultants.map((consultant) => (
|
||||||
|
<div
|
||||||
|
key={consultant.id}
|
||||||
|
className={`border rounded-lg p-4 cursor-pointer transition-colors ${
|
||||||
|
selectedConsultant === consultant.id
|
||||||
|
? "border-blue-500 bg-blue-50"
|
||||||
|
: "border-gray-200 hover:border-gray-300"
|
||||||
|
}`}
|
||||||
|
onClick={() => setSelectedConsultant(consultant.id)}
|
||||||
|
>
|
||||||
|
<div className="flex items-center">
|
||||||
|
<div className="w-12 h-12 bg-gray-200 rounded-full flex items-center justify-center mr-4">
|
||||||
|
<span className="text-gray-600">👤</span>
|
||||||
|
</div>
|
||||||
|
<div className="flex-1">
|
||||||
|
<div className="font-semibold">
|
||||||
|
{consultant.name}
|
||||||
|
</div>
|
||||||
|
<div className="text-sm text-gray-600">
|
||||||
|
{consultant.title}
|
||||||
|
</div>
|
||||||
|
<div className="flex items-center text-sm text-gray-500">
|
||||||
|
<span className="mr-2">
|
||||||
|
⭐ {consultant.rating}
|
||||||
|
</span>
|
||||||
|
<span className="mr-2">
|
||||||
|
({consultant.reviews} đánh giá)
|
||||||
|
</span>
|
||||||
|
<span>{consultant.experience}</span>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
))}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
{/* Date & Time */}
|
||||||
|
<div className="grid md:grid-cols-2 gap-6">
|
||||||
|
<div>
|
||||||
|
<label className="block text-sm font-medium text-gray-700 mb-2">
|
||||||
|
Ngày hẹn <span className="text-red-500">*</span>
|
||||||
|
</label>
|
||||||
|
<input
|
||||||
|
type="date"
|
||||||
|
value={selectedDate}
|
||||||
|
onChange={(e) => setSelectedDate(e.target.value)}
|
||||||
|
min={new Date().toISOString().split("T")[0]}
|
||||||
|
required
|
||||||
|
className="w-full px-3 py-2 border border-gray-300 rounded-lg focus:ring-2 focus:ring-blue-500 focus:border-transparent"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div>
|
||||||
|
<label className="block text-sm font-medium text-gray-700 mb-2">
|
||||||
|
Giờ hẹn <span className="text-red-500">*</span>
|
||||||
|
</label>
|
||||||
|
<select
|
||||||
|
value={selectedTime}
|
||||||
|
onChange={(e) => setSelectedTime(e.target.value)}
|
||||||
|
required
|
||||||
|
className="w-full px-3 py-2 border border-gray-300 rounded-lg focus:ring-2 focus:ring-blue-500 focus:border-transparent"
|
||||||
|
>
|
||||||
|
<option value="">Chọn giờ</option>
|
||||||
|
{appointmentData.timeSlots.map((slot) => (
|
||||||
|
<option
|
||||||
|
key={slot.time}
|
||||||
|
value={slot.time}
|
||||||
|
disabled={!slot.available}
|
||||||
|
>
|
||||||
|
{slot.label} {!slot.available && "(Đã đặt)"}
|
||||||
|
</option>
|
||||||
|
))}
|
||||||
|
</select>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
{/* Office Selection */}
|
||||||
|
<div>
|
||||||
|
<label className="block text-sm font-medium text-gray-700 mb-3">
|
||||||
|
Hình thức tư vấn <span className="text-red-500">*</span>
|
||||||
|
</label>
|
||||||
|
<div className="space-y-3">
|
||||||
|
<div
|
||||||
|
className={`border rounded-lg p-4 cursor-pointer transition-colors ${
|
||||||
|
selectedOffice === "online"
|
||||||
|
? "border-blue-500 bg-blue-50"
|
||||||
|
: "border-gray-200 hover:border-gray-300"
|
||||||
|
}`}
|
||||||
|
onClick={() => setSelectedOffice("online")}
|
||||||
|
>
|
||||||
|
<div className="flex items-center">
|
||||||
|
<span className="text-2xl mr-3">💻</span>
|
||||||
|
<div>
|
||||||
|
<div className="font-medium">Tư vấn online</div>
|
||||||
|
<div className="text-sm text-gray-600">
|
||||||
|
Video call qua Zoom/Google Meet
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
{appointmentData.offices.map((office, index) => (
|
||||||
|
<div
|
||||||
|
key={index}
|
||||||
|
className={`border rounded-lg p-4 cursor-pointer transition-colors ${
|
||||||
|
selectedOffice === office.name
|
||||||
|
? "border-blue-500 bg-blue-50"
|
||||||
|
: "border-gray-200 hover:border-gray-300"
|
||||||
|
}`}
|
||||||
|
onClick={() => setSelectedOffice(office.name)}
|
||||||
|
>
|
||||||
|
<div className="flex items-center">
|
||||||
|
<span className="text-2xl mr-3">🏢</span>
|
||||||
|
<div>
|
||||||
|
<div className="font-medium">
|
||||||
|
Văn phòng {office.name}
|
||||||
|
</div>
|
||||||
|
<div className="text-sm text-gray-600">
|
||||||
|
{office.address}
|
||||||
|
</div>
|
||||||
|
<div className="text-sm text-gray-500">
|
||||||
|
{office.hours}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
))}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
{/* Personal Information */}
|
||||||
|
<div className="border-t pt-6">
|
||||||
|
<h3 className="text-lg font-semibold text-gray-900 mb-4">
|
||||||
|
Thông Tin Cá Nhân
|
||||||
|
</h3>
|
||||||
|
<div className="grid md:grid-cols-2 gap-6">
|
||||||
|
{appointmentData.formFields.map((field) => (
|
||||||
|
<div
|
||||||
|
key={field.name}
|
||||||
|
className={
|
||||||
|
field.type === "textarea" ? "md:col-span-2" : ""
|
||||||
|
}
|
||||||
|
>
|
||||||
|
<label className="block text-sm font-medium text-gray-700 mb-2">
|
||||||
|
{field.label}
|
||||||
|
{field.required && (
|
||||||
|
<span className="text-red-500 ml-1">*</span>
|
||||||
|
)}
|
||||||
|
</label>
|
||||||
|
|
||||||
|
{field.type === "textarea" ? (
|
||||||
|
<textarea
|
||||||
|
name={field.name}
|
||||||
|
value={
|
||||||
|
formData[field.name as keyof typeof formData]
|
||||||
|
}
|
||||||
|
onChange={handleInputChange}
|
||||||
|
placeholder={field.placeholder}
|
||||||
|
required={field.required}
|
||||||
|
rows={4}
|
||||||
|
className="w-full px-3 py-2 border border-gray-300 rounded-lg focus:ring-2 focus:ring-blue-500 focus:border-transparent"
|
||||||
|
/>
|
||||||
|
) : field.type === "select" ? (
|
||||||
|
<select
|
||||||
|
name={field.name}
|
||||||
|
value={
|
||||||
|
formData[field.name as keyof typeof formData]
|
||||||
|
}
|
||||||
|
onChange={handleInputChange}
|
||||||
|
required={field.required}
|
||||||
|
className="w-full px-3 py-2 border border-gray-300 rounded-lg focus:ring-2 focus:ring-blue-500 focus:border-transparent"
|
||||||
|
>
|
||||||
|
<option value="">
|
||||||
|
Chọn {field.label.toLowerCase()}
|
||||||
|
</option>
|
||||||
|
{field.options?.map((option, index) => (
|
||||||
|
<option key={index} value={option}>
|
||||||
|
{option}
|
||||||
|
</option>
|
||||||
|
))}
|
||||||
|
</select>
|
||||||
|
) : (
|
||||||
|
<input
|
||||||
|
type={field.type}
|
||||||
|
name={field.name}
|
||||||
|
value={
|
||||||
|
formData[field.name as keyof typeof formData]
|
||||||
|
}
|
||||||
|
onChange={handleInputChange}
|
||||||
|
placeholder={field.placeholder}
|
||||||
|
required={field.required}
|
||||||
|
className="w-full px-3 py-2 border border-gray-300 rounded-lg focus:ring-2 focus:ring-blue-500 focus:border-transparent"
|
||||||
|
/>
|
||||||
|
)}
|
||||||
|
</div>
|
||||||
|
))}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
{/* Submit Button */}
|
||||||
|
<button
|
||||||
|
type="submit"
|
||||||
|
className="w-full bg-blue-600 text-white py-3 px-6 rounded-lg hover:bg-blue-700 transition-colors font-medium text-lg"
|
||||||
|
>
|
||||||
|
Đặt Lịch Hẹn
|
||||||
|
</button>
|
||||||
|
</form>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
{/* Sidebar */}
|
||||||
|
<div className="space-y-6">
|
||||||
|
{/* Booking Summary */}
|
||||||
|
<div className="bg-white rounded-lg shadow-lg p-6">
|
||||||
|
<h3 className="text-lg font-semibold text-gray-900 mb-4">
|
||||||
|
Tóm Tắt Đặt Lịch
|
||||||
|
</h3>
|
||||||
|
|
||||||
|
{selectedAppointmentType && (
|
||||||
|
<div className="space-y-3">
|
||||||
|
<div className="flex justify-between">
|
||||||
|
<span className="text-gray-600">Loại tư vấn:</span>
|
||||||
|
<span className="font-medium">
|
||||||
|
{selectedAppointmentType.name}
|
||||||
|
</span>
|
||||||
|
</div>
|
||||||
|
<div className="flex justify-between">
|
||||||
|
<span className="text-gray-600">Thời gian:</span>
|
||||||
|
<span className="font-medium">
|
||||||
|
{selectedAppointmentType.duration}
|
||||||
|
</span>
|
||||||
|
</div>
|
||||||
|
<div className="flex justify-between">
|
||||||
|
<span className="text-gray-600">Phí:</span>
|
||||||
|
<span className="font-medium text-blue-600">
|
||||||
|
{selectedAppointmentType.price}
|
||||||
|
</span>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
{selectedConsultantData && (
|
||||||
|
<div className="border-t pt-3">
|
||||||
|
<div className="flex justify-between">
|
||||||
|
<span className="text-gray-600">Chuyên gia:</span>
|
||||||
|
<span className="font-medium">
|
||||||
|
{selectedConsultantData.name}
|
||||||
|
</span>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
|
|
||||||
|
{selectedDate && selectedTime && (
|
||||||
|
<div className="border-t pt-3">
|
||||||
|
<div className="flex justify-between">
|
||||||
|
<span className="text-gray-600">Ngày giờ:</span>
|
||||||
|
<span className="font-medium">
|
||||||
|
{selectedDate} lúc{" "}
|
||||||
|
{
|
||||||
|
appointmentData.timeSlots.find(
|
||||||
|
(s) => s.time === selectedTime,
|
||||||
|
)?.label
|
||||||
|
}
|
||||||
|
</span>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
|
|
||||||
|
{selectedOffice && (
|
||||||
|
<div className="flex justify-between">
|
||||||
|
<span className="text-gray-600">Hình thức:</span>
|
||||||
|
<span className="font-medium">
|
||||||
|
{selectedOffice === "online"
|
||||||
|
? "Online"
|
||||||
|
: `Văn phòng ${selectedOffice}`}
|
||||||
|
</span>
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
|
</div>
|
||||||
|
|
||||||
|
{/* Contact Info */}
|
||||||
|
<div className="bg-blue-50 rounded-lg p-6">
|
||||||
|
<h3 className="text-lg font-semibold text-gray-900 mb-4">
|
||||||
|
Cần Hỗ Trợ?
|
||||||
|
</h3>
|
||||||
|
<div className="space-y-3">
|
||||||
|
<div className="flex items-center">
|
||||||
|
<span className="text-blue-600 mr-2">📞</span>
|
||||||
|
<span>1900 1234</span>
|
||||||
|
</div>
|
||||||
|
<div className="flex items-center">
|
||||||
|
<span className="text-blue-600 mr-2">✉️</span>
|
||||||
|
<span>info@visaservice.com</span>
|
||||||
|
</div>
|
||||||
|
<div className="flex items-center">
|
||||||
|
<span className="text-blue-600 mr-2">💬</span>
|
||||||
|
<span>Chat trực tuyến 24/7</span>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
{/* FAQs */}
|
||||||
|
<div className="mt-16">
|
||||||
|
<h2 className="text-2xl font-bold text-gray-900 mb-8 text-center">
|
||||||
|
Câu Hỏi Thường Gặp
|
||||||
|
</h2>
|
||||||
|
<div className="max-w-3xl mx-auto space-y-4">
|
||||||
|
{appointmentData.faqs.map((faq, index) => (
|
||||||
|
<div
|
||||||
|
key={index}
|
||||||
|
className="bg-white rounded-lg shadow-md overflow-hidden"
|
||||||
|
>
|
||||||
|
<button
|
||||||
|
className="w-full p-6 text-left hover:bg-gray-50 transition-colors"
|
||||||
|
onClick={() => toggleFaq(index)}
|
||||||
|
>
|
||||||
|
<div className="flex items-center justify-between">
|
||||||
|
<h3 className="font-semibold text-gray-900">
|
||||||
|
{faq.question}
|
||||||
|
</h3>
|
||||||
|
<span className="text-gray-400">
|
||||||
|
{openFaq === index ? "−" : "+"}
|
||||||
|
</span>
|
||||||
|
</div>
|
||||||
|
</button>
|
||||||
|
{openFaq === index && (
|
||||||
|
<div className="px-6 pb-6">
|
||||||
|
<p className="text-gray-700">{faq.answer}</p>
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
|
</div>
|
||||||
|
))}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
}
|
||||||
93
app/blog/blog.json
Normal file
93
app/blog/blog.json
Normal file
@@ -0,0 +1,93 @@
|
|||||||
|
{
|
||||||
|
"title": "Blog & Tin Tức",
|
||||||
|
"subtitle": "Cập nhật thông tin mới nhất về visa và du lịch",
|
||||||
|
"featured": {
|
||||||
|
"id": "visa-schengen-2024",
|
||||||
|
"title": "Hướng Dẫn Xin Visa Schengen 2024 - Cập Nhật Mới Nhất",
|
||||||
|
"excerpt": "Thủ tục xin visa Schengen đã có những thay đổi quan trọng trong năm 2024. Cùng tìm hiểu chi tiết...",
|
||||||
|
"image": "/images/schengen-visa.jpg",
|
||||||
|
"date": "2024-01-15",
|
||||||
|
"author": "Nguyễn Văn A",
|
||||||
|
"category": "Visa",
|
||||||
|
"readTime": "5 phút đọc"
|
||||||
|
},
|
||||||
|
"categories": [
|
||||||
|
{
|
||||||
|
"name": "Tất cả",
|
||||||
|
"slug": "all",
|
||||||
|
"count": 24
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "Visa",
|
||||||
|
"slug": "visa",
|
||||||
|
"count": 12
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "Du lịch",
|
||||||
|
"slug": "travel",
|
||||||
|
"count": 8
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "Thủ tục",
|
||||||
|
"slug": "procedures",
|
||||||
|
"count": 4
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"posts": [
|
||||||
|
{
|
||||||
|
"id": "visa-my-2024",
|
||||||
|
"title": "Thay Đổi Mới Trong Thủ Tục Xin Visa Mỹ 2024",
|
||||||
|
"excerpt": "Lãnh sự quán Mỹ đã công bố những thay đổi quan trọng trong quy trình xin visa...",
|
||||||
|
"image": "/images/us-visa.jpg",
|
||||||
|
"date": "2024-01-10",
|
||||||
|
"author": "Trần Thị B",
|
||||||
|
"category": "Visa",
|
||||||
|
"readTime": "7 phút đọc",
|
||||||
|
"tags": ["Visa Mỹ", "Thủ tục", "2024"]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "du-lich-nhat-ban",
|
||||||
|
"title": "Top 10 Địa Điểm Du Lịch Nhật Bản Không Thể Bỏ Qua",
|
||||||
|
"excerpt": "Khám phá những điểm đến tuyệt vời nhất tại đất nước mặt trời mọc...",
|
||||||
|
"image": "/images/japan-travel.jpg",
|
||||||
|
"date": "2024-01-08",
|
||||||
|
"author": "Lê Văn C",
|
||||||
|
"category": "Du lịch",
|
||||||
|
"readTime": "6 phút đọc",
|
||||||
|
"tags": ["Nhật Bản", "Du lịch", "Điểm đến"]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "visa-han-quoc-tips",
|
||||||
|
"title": "Bí Quyết Xin Visa Hàn Quốc Thành Công 100%",
|
||||||
|
"excerpt": "Những kinh nghiệm quý báu từ chuyên gia để tăng tỷ lệ thành công...",
|
||||||
|
"image": "/images/korea-visa.jpg",
|
||||||
|
"date": "2024-01-05",
|
||||||
|
"author": "Phạm Thị D",
|
||||||
|
"category": "Visa",
|
||||||
|
"readTime": "8 phút đọc",
|
||||||
|
"tags": ["Visa Hàn Quốc", "Kinh nghiệm", "Thành công"]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "du-lich-chau-au",
|
||||||
|
"title": "Lịch Trình Du Lịch Châu Âu 14 Ngày Hoàn Hảo",
|
||||||
|
"excerpt": "Khám phá 7 quốc gia châu Âu với lịch trình được thiết kế tối ưu...",
|
||||||
|
"image": "/images/europe-travel.jpg",
|
||||||
|
"date": "2024-01-03",
|
||||||
|
"author": "Hoàng Văn E",
|
||||||
|
"category": "Du lịch",
|
||||||
|
"readTime": "10 phút đọc",
|
||||||
|
"tags": ["Châu Âu", "Lịch trình", "14 ngày"]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "thu-tuc-ho-so",
|
||||||
|
"title": "Checklist Hồ Sơ Xin Visa - Không Bỏ Sót Gì",
|
||||||
|
"excerpt": "Danh sách chi tiết các giấy tờ cần thiết cho từng loại visa...",
|
||||||
|
"image": "/images/documents.jpg",
|
||||||
|
"date": "2024-01-01",
|
||||||
|
"author": "Vũ Thị F",
|
||||||
|
"category": "Thủ tục",
|
||||||
|
"readTime": "4 phút đọc",
|
||||||
|
"tags": ["Hồ sơ", "Checklist", "Visa"]
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
144
app/blog/page.tsx
Normal file
144
app/blog/page.tsx
Normal file
@@ -0,0 +1,144 @@
|
|||||||
|
"use client";
|
||||||
|
|
||||||
|
import { useState } from "react";
|
||||||
|
import blogData from "./blog.json";
|
||||||
|
|
||||||
|
export default function BlogPage() {
|
||||||
|
const [selectedCategory, setSelectedCategory] = useState("all");
|
||||||
|
|
||||||
|
const filteredPosts =
|
||||||
|
selectedCategory === "all"
|
||||||
|
? blogData.posts
|
||||||
|
: blogData.posts.filter(
|
||||||
|
(post) => post.category.toLowerCase() === selectedCategory,
|
||||||
|
);
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div className="container mx-auto px-4 py-8">
|
||||||
|
<div className="max-w-6xl mx-auto">
|
||||||
|
{/* Header */}
|
||||||
|
<div className="text-center mb-12">
|
||||||
|
<h1 className="text-4xl font-bold text-gray-900 mb-4">
|
||||||
|
{blogData.title}
|
||||||
|
</h1>
|
||||||
|
<p className="text-xl text-gray-600">{blogData.subtitle}</p>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
{/* Featured Post */}
|
||||||
|
<div className="mb-12">
|
||||||
|
<div className="bg-gradient-to-r from-blue-600 to-purple-600 rounded-lg p-8 text-white">
|
||||||
|
<div className="flex items-center mb-4">
|
||||||
|
<span className="bg-yellow-400 text-black px-3 py-1 rounded-full text-sm font-medium mr-4">
|
||||||
|
Nổi bật
|
||||||
|
</span>
|
||||||
|
<span className="text-blue-100">
|
||||||
|
{blogData.featured.category}
|
||||||
|
</span>
|
||||||
|
</div>
|
||||||
|
<h2 className="text-3xl font-bold mb-4">
|
||||||
|
{blogData.featured.title}
|
||||||
|
</h2>
|
||||||
|
<p className="text-blue-100 mb-6 text-lg">
|
||||||
|
{blogData.featured.excerpt}
|
||||||
|
</p>
|
||||||
|
<div className="flex items-center justify-between">
|
||||||
|
<div className="flex items-center text-blue-100">
|
||||||
|
<span className="mr-4">👤 {blogData.featured.author}</span>
|
||||||
|
<span className="mr-4">📅 {blogData.featured.date}</span>
|
||||||
|
<span>⏱️ {blogData.featured.readTime}</span>
|
||||||
|
</div>
|
||||||
|
<button className="bg-white text-blue-600 px-6 py-2 rounded-lg hover:bg-blue-50 transition-colors">
|
||||||
|
Đọc thêm
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
{/* Category Filter */}
|
||||||
|
<div className="mb-8">
|
||||||
|
<div className="flex flex-wrap gap-4 justify-center">
|
||||||
|
{blogData.categories.map((category) => (
|
||||||
|
<button
|
||||||
|
key={category.slug}
|
||||||
|
onClick={() => setSelectedCategory(category.slug)}
|
||||||
|
className={`px-6 py-2 rounded-full transition-colors ${
|
||||||
|
selectedCategory === category.slug
|
||||||
|
? "bg-blue-600 text-white"
|
||||||
|
: "bg-gray-100 text-gray-700 hover:bg-gray-200"
|
||||||
|
}`}
|
||||||
|
>
|
||||||
|
{category.name} ({category.count})
|
||||||
|
</button>
|
||||||
|
))}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
{/* Blog Posts Grid */}
|
||||||
|
<div className="grid md:grid-cols-2 lg:grid-cols-3 gap-8">
|
||||||
|
{filteredPosts.map((post) => (
|
||||||
|
<article
|
||||||
|
key={post.id}
|
||||||
|
className="bg-white rounded-lg shadow-md overflow-hidden hover:shadow-lg transition-shadow"
|
||||||
|
>
|
||||||
|
{/* Post Image */}
|
||||||
|
<div className="h-48 bg-gray-200 flex items-center justify-center">
|
||||||
|
<span className="text-gray-400">📷 {post.category}</span>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
{/* Post Content */}
|
||||||
|
<div className="p-6">
|
||||||
|
{/* Category & Date */}
|
||||||
|
<div className="flex items-center justify-between mb-3">
|
||||||
|
<span className="bg-blue-100 text-blue-800 px-2 py-1 rounded text-sm">
|
||||||
|
{post.category}
|
||||||
|
</span>
|
||||||
|
<span className="text-gray-500 text-sm">{post.date}</span>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
{/* Title */}
|
||||||
|
<h3 className="text-xl font-semibold text-gray-900 mb-3 line-clamp-2">
|
||||||
|
{post.title}
|
||||||
|
</h3>
|
||||||
|
|
||||||
|
{/* Excerpt */}
|
||||||
|
<p className="text-gray-700 mb-4 line-clamp-3">
|
||||||
|
{post.excerpt}
|
||||||
|
</p>
|
||||||
|
|
||||||
|
{/* Tags */}
|
||||||
|
<div className="flex flex-wrap gap-2 mb-4">
|
||||||
|
{post.tags.map((tag, index) => (
|
||||||
|
<span
|
||||||
|
key={index}
|
||||||
|
className="bg-gray-100 text-gray-600 px-2 py-1 rounded text-xs"
|
||||||
|
>
|
||||||
|
#{tag}
|
||||||
|
</span>
|
||||||
|
))}
|
||||||
|
</div>
|
||||||
|
|
||||||
|
{/* Meta Info */}
|
||||||
|
<div className="flex items-center justify-between text-sm text-gray-500">
|
||||||
|
<span>👤 {post.author}</span>
|
||||||
|
<span>⏱️ {post.readTime}</span>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
{/* Read More Button */}
|
||||||
|
<button className="w-full mt-4 bg-blue-600 text-white py-2 px-4 rounded-lg hover:bg-blue-700 transition-colors">
|
||||||
|
Đọc thêm
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
</article>
|
||||||
|
))}
|
||||||
|
</div>
|
||||||
|
|
||||||
|
{/* Load More */}
|
||||||
|
<div className="text-center mt-12">
|
||||||
|
<button className="bg-gray-100 text-gray-700 px-8 py-3 rounded-lg hover:bg-gray-200 transition-colors">
|
||||||
|
Xem thêm bài viết
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
}
|
||||||
191
app/components/Footer.tsx
Normal file
191
app/components/Footer.tsx
Normal file
@@ -0,0 +1,191 @@
|
|||||||
|
import Link from "next/link";
|
||||||
|
|
||||||
|
export default function Footer() {
|
||||||
|
const currentYear = new Date().getFullYear();
|
||||||
|
|
||||||
|
const quickLinks = [
|
||||||
|
{ name: "Về chúng tôi", href: "/about" },
|
||||||
|
{ name: "Dịch vụ", href: "/services" },
|
||||||
|
{ name: "Bảng giá", href: "/pricing" },
|
||||||
|
{ name: "Blog", href: "/blog" },
|
||||||
|
{ name: "Liên hệ", href: "/contact" },
|
||||||
|
];
|
||||||
|
|
||||||
|
const visaServices = [
|
||||||
|
{ name: "Visa Mỹ", href: "/visa" },
|
||||||
|
{ name: "Visa Schengen", href: "/visa" },
|
||||||
|
{ name: "Visa Nhật Bản", href: "/visa" },
|
||||||
|
{ name: "Visa Hàn Quốc", href: "/visa" },
|
||||||
|
{ name: "Visa Canada", href: "/visa" },
|
||||||
|
];
|
||||||
|
|
||||||
|
const countries = [
|
||||||
|
{ name: "Châu Âu", href: "/countries" },
|
||||||
|
{ name: "Bắc Mỹ", href: "/countries" },
|
||||||
|
{ name: "Châu Á", href: "/countries" },
|
||||||
|
{ name: "Châu Đại Dương", href: "/countries" },
|
||||||
|
];
|
||||||
|
|
||||||
|
return (
|
||||||
|
<footer className="bg-gray-900 text-white">
|
||||||
|
<div className="container mx-auto px-4 py-12">
|
||||||
|
<div className="grid md:grid-cols-2 lg:grid-cols-4 gap-8">
|
||||||
|
{/* Company Info */}
|
||||||
|
<div>
|
||||||
|
<div className="flex items-center space-x-2 mb-6">
|
||||||
|
<div className="w-10 h-10 bg-blue-600 rounded-lg flex items-center justify-center">
|
||||||
|
<span className="text-white font-bold text-xl">V</span>
|
||||||
|
</div>
|
||||||
|
<div>
|
||||||
|
<div className="font-bold text-xl">VisaService</div>
|
||||||
|
<div className="text-sm text-gray-400">Dịch vụ visa uy tín</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<p className="text-gray-400 mb-6">
|
||||||
|
Đối tác tin cậy cho dịch vụ visa và du lịch với hơn 10 năm kinh
|
||||||
|
nghiệm. Cam kết mang đến dịch vụ chất lượng cao với tỷ lệ thành
|
||||||
|
công 99%.
|
||||||
|
</p>
|
||||||
|
<div className="flex space-x-4">
|
||||||
|
<a
|
||||||
|
href="#"
|
||||||
|
className="text-gray-400 hover:text-white transition-colors"
|
||||||
|
>
|
||||||
|
<span className="sr-only">Facebook</span>
|
||||||
|
📘
|
||||||
|
</a>
|
||||||
|
<a
|
||||||
|
href="#"
|
||||||
|
className="text-gray-400 hover:text-white transition-colors"
|
||||||
|
>
|
||||||
|
<span className="sr-only">Instagram</span>
|
||||||
|
📷
|
||||||
|
</a>
|
||||||
|
<a
|
||||||
|
href="#"
|
||||||
|
className="text-gray-400 hover:text-white transition-colors"
|
||||||
|
>
|
||||||
|
<span className="sr-only">YouTube</span>
|
||||||
|
📺
|
||||||
|
</a>
|
||||||
|
<a
|
||||||
|
href="#"
|
||||||
|
className="text-gray-400 hover:text-white transition-colors"
|
||||||
|
>
|
||||||
|
<span className="sr-only">LinkedIn</span>
|
||||||
|
💼
|
||||||
|
</a>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
{/* Quick Links */}
|
||||||
|
<div>
|
||||||
|
<h3 className="font-semibold text-lg mb-6">Liên Kết Nhanh</h3>
|
||||||
|
<ul className="space-y-3">
|
||||||
|
{quickLinks.map((link) => (
|
||||||
|
<li key={link.name}>
|
||||||
|
<Link
|
||||||
|
href={link.href}
|
||||||
|
className="text-gray-400 hover:text-white transition-colors"
|
||||||
|
>
|
||||||
|
{link.name}
|
||||||
|
</Link>
|
||||||
|
</li>
|
||||||
|
))}
|
||||||
|
</ul>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
{/* Visa Services */}
|
||||||
|
<div>
|
||||||
|
<h3 className="font-semibold text-lg mb-6">Dịch Vụ Visa</h3>
|
||||||
|
<ul className="space-y-3">
|
||||||
|
{visaServices.map((service) => (
|
||||||
|
<li key={service.name}>
|
||||||
|
<Link
|
||||||
|
href={service.href}
|
||||||
|
className="text-gray-400 hover:text-white transition-colors"
|
||||||
|
>
|
||||||
|
{service.name}
|
||||||
|
</Link>
|
||||||
|
</li>
|
||||||
|
))}
|
||||||
|
</ul>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
{/* Contact Info */}
|
||||||
|
<div>
|
||||||
|
<h3 className="font-semibold text-lg mb-6">Thông Tin Liên Hệ</h3>
|
||||||
|
<div className="space-y-4">
|
||||||
|
<div className="flex items-start">
|
||||||
|
<span className="text-blue-400 mr-3 mt-1">📍</span>
|
||||||
|
<div>
|
||||||
|
<p className="text-gray-400">
|
||||||
|
123 Nguyễn Huệ, Quận 1<br />
|
||||||
|
TP. Hồ Chí Minh
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div className="flex items-center">
|
||||||
|
<span className="text-blue-400 mr-3">📞</span>
|
||||||
|
<p className="text-gray-400">+84 28 1234 5678</p>
|
||||||
|
</div>
|
||||||
|
<div className="flex items-center">
|
||||||
|
<span className="text-blue-400 mr-3">✉️</span>
|
||||||
|
<p className="text-gray-400">info@visaservice.com</p>
|
||||||
|
</div>
|
||||||
|
<div className="flex items-start">
|
||||||
|
<span className="text-blue-400 mr-3 mt-1">🕒</span>
|
||||||
|
<div>
|
||||||
|
<p className="text-gray-400">
|
||||||
|
Thứ 2 - Thứ 6: 8:00 - 18:00
|
||||||
|
<br />
|
||||||
|
Thứ 7: 8:00 - 12:00
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
{/* Bottom Section */}
|
||||||
|
<div className="border-t border-gray-800 mt-12 pt-8">
|
||||||
|
<div className="flex flex-col md:flex-row justify-between items-center">
|
||||||
|
<div className="text-gray-400 text-sm mb-4 md:mb-0">
|
||||||
|
© {currentYear} VisaService. Tất cả quyền được bảo lưu.
|
||||||
|
</div>
|
||||||
|
<div className="flex space-x-6 text-sm">
|
||||||
|
<Link
|
||||||
|
href="#"
|
||||||
|
className="text-gray-400 hover:text-white transition-colors"
|
||||||
|
>
|
||||||
|
Chính sách bảo mật
|
||||||
|
</Link>
|
||||||
|
<Link
|
||||||
|
href="#"
|
||||||
|
className="text-gray-400 hover:text-white transition-colors"
|
||||||
|
>
|
||||||
|
Điều khoản sử dụng
|
||||||
|
</Link>
|
||||||
|
<Link
|
||||||
|
href="#"
|
||||||
|
className="text-gray-400 hover:text-white transition-colors"
|
||||||
|
>
|
||||||
|
Sitemap
|
||||||
|
</Link>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
{/* Floating Contact Button */}
|
||||||
|
<div className="fixed bottom-6 right-6 z-50">
|
||||||
|
<Link
|
||||||
|
href="/appointment"
|
||||||
|
className="bg-green-500 text-white p-4 rounded-full shadow-lg hover:bg-green-600 transition-colors flex items-center justify-center"
|
||||||
|
>
|
||||||
|
<span className="text-2xl">💬</span>
|
||||||
|
</Link>
|
||||||
|
</div>
|
||||||
|
</footer>
|
||||||
|
);
|
||||||
|
}
|
||||||
114
app/components/Header.tsx
Normal file
114
app/components/Header.tsx
Normal file
@@ -0,0 +1,114 @@
|
|||||||
|
"use client";
|
||||||
|
|
||||||
|
import { useState } from "react";
|
||||||
|
import Link from "next/link";
|
||||||
|
|
||||||
|
export default function Header() {
|
||||||
|
const [isMenuOpen, setIsMenuOpen] = useState(false);
|
||||||
|
|
||||||
|
const navigation = [
|
||||||
|
{ name: "Trang chủ", href: "/" },
|
||||||
|
{ name: "Về chúng tôi", href: "/about" },
|
||||||
|
{ name: "Dịch vụ", href: "/services" },
|
||||||
|
{ name: "Visa", href: "/visa" },
|
||||||
|
{ name: "Quốc gia", href: "/countries" },
|
||||||
|
{ name: "Bảng giá", href: "/pricing" },
|
||||||
|
{ name: "Blog", href: "/blog" },
|
||||||
|
{ name: "Liên hệ", href: "/contact" },
|
||||||
|
];
|
||||||
|
|
||||||
|
return (
|
||||||
|
<header className="bg-white shadow-lg sticky top-0 z-50">
|
||||||
|
<div className="container mx-auto px-4">
|
||||||
|
<div className="flex items-center justify-between h-16">
|
||||||
|
{/* Logo */}
|
||||||
|
<Link href="/" className="flex items-center space-x-2">
|
||||||
|
<div className="w-10 h-10 bg-blue-600 rounded-lg flex items-center justify-center">
|
||||||
|
<span className="text-white font-bold text-xl">V</span>
|
||||||
|
</div>
|
||||||
|
<div>
|
||||||
|
<div className="font-bold text-xl text-gray-900">VisaService</div>
|
||||||
|
<div className="text-xs text-gray-500">Dịch vụ visa uy tín</div>
|
||||||
|
</div>
|
||||||
|
</Link>
|
||||||
|
|
||||||
|
{/* Desktop Navigation */}
|
||||||
|
<nav className="hidden lg:flex items-center space-x-8">
|
||||||
|
{navigation.map((item) => (
|
||||||
|
<Link
|
||||||
|
key={item.name}
|
||||||
|
href={item.href}
|
||||||
|
className="text-gray-700 hover:text-blue-600 transition-colors font-medium"
|
||||||
|
>
|
||||||
|
{item.name}
|
||||||
|
</Link>
|
||||||
|
))}
|
||||||
|
</nav>
|
||||||
|
|
||||||
|
{/* CTA Buttons */}
|
||||||
|
<div className="hidden lg:flex items-center space-x-4">
|
||||||
|
<Link
|
||||||
|
href="/appointment"
|
||||||
|
className="bg-blue-600 text-white px-6 py-2 rounded-lg hover:bg-blue-700 transition-colors font-medium"
|
||||||
|
>
|
||||||
|
Đặt lịch tư vấn
|
||||||
|
</Link>
|
||||||
|
<div className="flex items-center text-gray-600">
|
||||||
|
<span className="text-sm">📞</span>
|
||||||
|
<span className="ml-1 font-medium">1900 1234</span>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
{/* Mobile Menu Button */}
|
||||||
|
<button
|
||||||
|
className="lg:hidden p-2"
|
||||||
|
onClick={() => setIsMenuOpen(!isMenuOpen)}
|
||||||
|
>
|
||||||
|
<div className="w-6 h-6 flex flex-col justify-center items-center">
|
||||||
|
<span
|
||||||
|
className={`bg-gray-600 block transition-all duration-300 ease-out h-0.5 w-6 rounded-sm ${isMenuOpen ? "rotate-45 translate-y-1" : "-translate-y-0.5"}`}
|
||||||
|
></span>
|
||||||
|
<span
|
||||||
|
className={`bg-gray-600 block transition-all duration-300 ease-out h-0.5 w-6 rounded-sm my-0.5 ${isMenuOpen ? "opacity-0" : "opacity-100"}`}
|
||||||
|
></span>
|
||||||
|
<span
|
||||||
|
className={`bg-gray-600 block transition-all duration-300 ease-out h-0.5 w-6 rounded-sm ${isMenuOpen ? "-rotate-45 -translate-y-1" : "translate-y-0.5"}`}
|
||||||
|
></span>
|
||||||
|
</div>
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
{/* Mobile Menu */}
|
||||||
|
{isMenuOpen && (
|
||||||
|
<div className="lg:hidden border-t border-gray-200">
|
||||||
|
<div className="py-4 space-y-4">
|
||||||
|
{navigation.map((item) => (
|
||||||
|
<Link
|
||||||
|
key={item.name}
|
||||||
|
href={item.href}
|
||||||
|
className="block text-gray-700 hover:text-blue-600 transition-colors font-medium"
|
||||||
|
onClick={() => setIsMenuOpen(false)}
|
||||||
|
>
|
||||||
|
{item.name}
|
||||||
|
</Link>
|
||||||
|
))}
|
||||||
|
<div className="pt-4 border-t border-gray-200">
|
||||||
|
<Link
|
||||||
|
href="/appointment"
|
||||||
|
className="block bg-blue-600 text-white px-6 py-3 rounded-lg hover:bg-blue-700 transition-colors font-medium text-center mb-4"
|
||||||
|
onClick={() => setIsMenuOpen(false)}
|
||||||
|
>
|
||||||
|
Đặt lịch tư vấn
|
||||||
|
</Link>
|
||||||
|
<div className="flex items-center justify-center text-gray-600">
|
||||||
|
<span className="text-sm">📞</span>
|
||||||
|
<span className="ml-1 font-medium">1900 1234</span>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
|
</div>
|
||||||
|
</header>
|
||||||
|
);
|
||||||
|
}
|
||||||
89
app/contact/contact.json
Normal file
89
app/contact/contact.json
Normal file
@@ -0,0 +1,89 @@
|
|||||||
|
{
|
||||||
|
"title": "Liên Hệ Với Chúng Tôi",
|
||||||
|
"subtitle": "Chúng tôi luôn sẵn sàng hỗ trợ bạn 24/7",
|
||||||
|
"contactInfo": {
|
||||||
|
"address": {
|
||||||
|
"label": "Địa chỉ",
|
||||||
|
"value": "123 Nguyễn Huệ, Quận 1, TP.HCM",
|
||||||
|
"icon": "📍"
|
||||||
|
},
|
||||||
|
"phone": {
|
||||||
|
"label": "Điện thoại",
|
||||||
|
"value": "+84 28 1234 5678",
|
||||||
|
"icon": "📞"
|
||||||
|
},
|
||||||
|
"email": {
|
||||||
|
"label": "Email",
|
||||||
|
"value": "info@visaservice.com",
|
||||||
|
"icon": "✉️"
|
||||||
|
},
|
||||||
|
"hours": {
|
||||||
|
"label": "Giờ làm việc",
|
||||||
|
"value": "Thứ 2 - Thứ 6: 8:00 - 18:00\nThứ 7: 8:00 - 12:00",
|
||||||
|
"icon": "🕒"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"offices": [
|
||||||
|
{
|
||||||
|
"name": "Văn Phòng TP.HCM",
|
||||||
|
"address": "123 Nguyễn Huệ, Quận 1, TP.HCM",
|
||||||
|
"phone": "+84 28 1234 5678",
|
||||||
|
"email": "hcm@visaservice.com"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "Văn Phòng Hà Nội",
|
||||||
|
"address": "456 Hoàn Kiếm, Hà Nội",
|
||||||
|
"phone": "+84 24 1234 5678",
|
||||||
|
"email": "hanoi@visaservice.com"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "Văn Phòng Đà Nẵng",
|
||||||
|
"address": "789 Hải Châu, Đà Nẵng",
|
||||||
|
"phone": "+84 236 1234 567",
|
||||||
|
"email": "danang@visaservice.com"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"formFields": [
|
||||||
|
{
|
||||||
|
"name": "fullName",
|
||||||
|
"label": "Họ và tên",
|
||||||
|
"type": "text",
|
||||||
|
"required": true,
|
||||||
|
"placeholder": "Nhập họ và tên của bạn"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "email",
|
||||||
|
"label": "Email",
|
||||||
|
"type": "email",
|
||||||
|
"required": true,
|
||||||
|
"placeholder": "example@email.com"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "phone",
|
||||||
|
"label": "Số điện thoại",
|
||||||
|
"type": "tel",
|
||||||
|
"required": true,
|
||||||
|
"placeholder": "+84 xxx xxx xxx"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "service",
|
||||||
|
"label": "Dịch vụ quan tâm",
|
||||||
|
"type": "select",
|
||||||
|
"required": true,
|
||||||
|
"options": [
|
||||||
|
"Tư vấn visa",
|
||||||
|
"Chuẩn bị hồ sơ",
|
||||||
|
"Đặt vé máy bay",
|
||||||
|
"Tour trọn gói",
|
||||||
|
"Khác"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "message",
|
||||||
|
"label": "Tin nhắn",
|
||||||
|
"type": "textarea",
|
||||||
|
"required": false,
|
||||||
|
"placeholder": "Mô tả chi tiết nhu cầu của bạn..."
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
157
app/contact/page.tsx
Normal file
157
app/contact/page.tsx
Normal file
@@ -0,0 +1,157 @@
|
|||||||
|
"use client";
|
||||||
|
|
||||||
|
import { useState } from "react";
|
||||||
|
import contactData from "./contact.json";
|
||||||
|
|
||||||
|
export default function ContactPage() {
|
||||||
|
const [formData, setFormData] = useState({
|
||||||
|
fullName: "",
|
||||||
|
email: "",
|
||||||
|
phone: "",
|
||||||
|
service: "",
|
||||||
|
message: "",
|
||||||
|
});
|
||||||
|
|
||||||
|
const handleInputChange = (
|
||||||
|
e: React.ChangeEvent<
|
||||||
|
HTMLInputElement | HTMLTextAreaElement | HTMLSelectElement
|
||||||
|
>,
|
||||||
|
) => {
|
||||||
|
const { name, value } = e.target;
|
||||||
|
setFormData((prev) => ({
|
||||||
|
...prev,
|
||||||
|
[name]: value,
|
||||||
|
}));
|
||||||
|
};
|
||||||
|
|
||||||
|
const handleSubmit = (e: React.FormEvent) => {
|
||||||
|
e.preventDefault();
|
||||||
|
console.log("Form submitted:", formData);
|
||||||
|
// Handle form submission here
|
||||||
|
alert(
|
||||||
|
"Cảm ơn bạn đã liên hệ! Chúng tôi sẽ phản hồi trong thời gian sớm nhất.",
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div className="container mx-auto px-4 py-8">
|
||||||
|
<div className="max-w-6xl mx-auto">
|
||||||
|
{/* Header */}
|
||||||
|
<div className="text-center mb-12">
|
||||||
|
<h1 className="text-4xl font-bold text-gray-900 mb-4">
|
||||||
|
{contactData.title}
|
||||||
|
</h1>
|
||||||
|
<p className="text-xl text-gray-600">{contactData.subtitle}</p>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div className="grid lg:grid-cols-2 gap-12">
|
||||||
|
{/* Contact Information */}
|
||||||
|
<div>
|
||||||
|
<h2 className="text-2xl font-bold text-gray-900 mb-6">
|
||||||
|
Thông Tin Liên Hệ
|
||||||
|
</h2>
|
||||||
|
|
||||||
|
{/* Contact Details */}
|
||||||
|
<div className="space-y-6 mb-8">
|
||||||
|
{Object.entries(contactData.contactInfo).map(([key, info]) => (
|
||||||
|
<div key={key} className="flex items-start">
|
||||||
|
<span className="text-2xl mr-4">{info.icon}</span>
|
||||||
|
<div>
|
||||||
|
<h3 className="font-semibold text-gray-900">
|
||||||
|
{info.label}
|
||||||
|
</h3>
|
||||||
|
<p className="text-gray-700 whitespace-pre-line">
|
||||||
|
{info.value}
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
))}
|
||||||
|
</div>
|
||||||
|
|
||||||
|
{/* Offices */}
|
||||||
|
<h3 className="text-xl font-bold text-gray-900 mb-4">Văn Phòng</h3>
|
||||||
|
<div className="space-y-4">
|
||||||
|
{contactData.offices.map((office, index) => (
|
||||||
|
<div key={index} className="bg-gray-50 p-4 rounded-lg">
|
||||||
|
<h4 className="font-semibold text-gray-900 mb-2">
|
||||||
|
{office.name}
|
||||||
|
</h4>
|
||||||
|
<p className="text-gray-700 text-sm mb-1">
|
||||||
|
📍 {office.address}
|
||||||
|
</p>
|
||||||
|
<p className="text-gray-700 text-sm mb-1">
|
||||||
|
📞 {office.phone}
|
||||||
|
</p>
|
||||||
|
<p className="text-gray-700 text-sm">✉️ {office.email}</p>
|
||||||
|
</div>
|
||||||
|
))}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
{/* Contact Form */}
|
||||||
|
<div>
|
||||||
|
<h2 className="text-2xl font-bold text-gray-900 mb-6">
|
||||||
|
Gửi Tin Nhắn
|
||||||
|
</h2>
|
||||||
|
<form onSubmit={handleSubmit} className="space-y-6">
|
||||||
|
{contactData.formFields.map((field) => (
|
||||||
|
<div key={field.name}>
|
||||||
|
<label className="block text-sm font-medium text-gray-700 mb-2">
|
||||||
|
{field.label}
|
||||||
|
{field.required && (
|
||||||
|
<span className="text-red-500 ml-1">*</span>
|
||||||
|
)}
|
||||||
|
</label>
|
||||||
|
|
||||||
|
{field.type === "textarea" ? (
|
||||||
|
<textarea
|
||||||
|
name={field.name}
|
||||||
|
value={formData[field.name as keyof typeof formData]}
|
||||||
|
onChange={handleInputChange}
|
||||||
|
placeholder={field.placeholder}
|
||||||
|
required={field.required}
|
||||||
|
rows={4}
|
||||||
|
className="w-full px-3 py-2 border border-gray-300 rounded-lg focus:ring-2 focus:ring-blue-500 focus:border-transparent"
|
||||||
|
/>
|
||||||
|
) : field.type === "select" ? (
|
||||||
|
<select
|
||||||
|
name={field.name}
|
||||||
|
value={formData[field.name as keyof typeof formData]}
|
||||||
|
onChange={handleInputChange}
|
||||||
|
required={field.required}
|
||||||
|
className="w-full px-3 py-2 border border-gray-300 rounded-lg focus:ring-2 focus:ring-blue-500 focus:border-transparent"
|
||||||
|
>
|
||||||
|
<option value="">Chọn dịch vụ</option>
|
||||||
|
{field.options?.map((option, index) => (
|
||||||
|
<option key={index} value={option}>
|
||||||
|
{option}
|
||||||
|
</option>
|
||||||
|
))}
|
||||||
|
</select>
|
||||||
|
) : (
|
||||||
|
<input
|
||||||
|
type={field.type}
|
||||||
|
name={field.name}
|
||||||
|
value={formData[field.name as keyof typeof formData]}
|
||||||
|
onChange={handleInputChange}
|
||||||
|
placeholder={field.placeholder}
|
||||||
|
required={field.required}
|
||||||
|
className="w-full px-3 py-2 border border-gray-300 rounded-lg focus:ring-2 focus:ring-blue-500 focus:border-transparent"
|
||||||
|
/>
|
||||||
|
)}
|
||||||
|
</div>
|
||||||
|
))}
|
||||||
|
|
||||||
|
<button
|
||||||
|
type="submit"
|
||||||
|
className="w-full bg-blue-600 text-white py-3 px-6 rounded-lg hover:bg-blue-700 transition-colors font-medium"
|
||||||
|
>
|
||||||
|
Gửi Tin Nhắn
|
||||||
|
</button>
|
||||||
|
</form>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
}
|
||||||
188
app/countries/countries.json
Normal file
188
app/countries/countries.json
Normal file
@@ -0,0 +1,188 @@
|
|||||||
|
{
|
||||||
|
"title": "Danh Sách Quốc Gia",
|
||||||
|
"subtitle": "Chúng tôi hỗ trợ visa cho hơn 50 quốc gia trên thế giới",
|
||||||
|
"regions": [
|
||||||
|
{
|
||||||
|
"name": "Châu Âu",
|
||||||
|
"countries": [
|
||||||
|
{
|
||||||
|
"id": "schengen",
|
||||||
|
"name": "Schengen",
|
||||||
|
"flag": "🇪🇺",
|
||||||
|
"visaTypes": ["Du lịch", "Công tác", "Thăm thân"],
|
||||||
|
"processingTime": "15-20 ngày",
|
||||||
|
"validityPeriod": "90 ngày",
|
||||||
|
"fee": "2,200,000 VNĐ",
|
||||||
|
"requirements": [
|
||||||
|
"Hộ chiếu còn hạn trên 6 tháng",
|
||||||
|
"Ảnh 3.5x4.5cm (2 tấm)",
|
||||||
|
"Bảo hiểm du lịch",
|
||||||
|
"Chứng minh tài chính",
|
||||||
|
"Lịch trình du lịch"
|
||||||
|
],
|
||||||
|
"description": "Visa Schengen cho phép du lịch tự do trong 26 quốc gia châu Âu"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "uk",
|
||||||
|
"name": "Anh",
|
||||||
|
"flag": "🇬🇧",
|
||||||
|
"visaTypes": ["Du lịch", "Công tác", "Học tập"],
|
||||||
|
"processingTime": "15-25 ngày",
|
||||||
|
"validityPeriod": "6 tháng",
|
||||||
|
"fee": "3,800,000 VNĐ",
|
||||||
|
"requirements": [
|
||||||
|
"Hộ chiếu còn hạn trên 6 tháng",
|
||||||
|
"Ảnh passport",
|
||||||
|
"Chứng minh tài chính",
|
||||||
|
"Thư mời (nếu có)",
|
||||||
|
"Bảo hiểm du lịch"
|
||||||
|
],
|
||||||
|
"description": "Visa Anh cho phép khám phá vương quốc Anh và Bắc Ireland"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "Bắc Mỹ",
|
||||||
|
"countries": [
|
||||||
|
{
|
||||||
|
"id": "usa",
|
||||||
|
"name": "Mỹ",
|
||||||
|
"flag": "🇺🇸",
|
||||||
|
"visaTypes": ["B1/B2", "F1", "J1"],
|
||||||
|
"processingTime": "3-5 tuần",
|
||||||
|
"validityPeriod": "10 năm",
|
||||||
|
"fee": "4,600,000 VNĐ",
|
||||||
|
"requirements": [
|
||||||
|
"Hộ chiếu còn hạn trên 6 tháng",
|
||||||
|
"Form DS-160",
|
||||||
|
"Ảnh visa Mỹ",
|
||||||
|
"Chứng minh tài chính",
|
||||||
|
"Phỏng vấn tại lãnh sự quán"
|
||||||
|
],
|
||||||
|
"description": "Visa Mỹ B1/B2 cho phép du lịch và công tác tại Hoa Kỳ"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "canada",
|
||||||
|
"name": "Canada",
|
||||||
|
"flag": "🇨🇦",
|
||||||
|
"visaTypes": ["TRV", "Study Permit", "Work Permit"],
|
||||||
|
"processingTime": "4-6 tuần",
|
||||||
|
"validityPeriod": "10 năm",
|
||||||
|
"fee": "2,800,000 VNĐ",
|
||||||
|
"requirements": [
|
||||||
|
"Hộ chiếu còn hạn trên 6 tháng",
|
||||||
|
"Ảnh passport",
|
||||||
|
"Chứng minh tài chính",
|
||||||
|
"Thư mời (nếu có)",
|
||||||
|
"Khám sức khỏe (nếu cần)"
|
||||||
|
],
|
||||||
|
"description": "Visa Canada cho phép du lịch và làm việc tại Canada"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "Châu Á",
|
||||||
|
"countries": [
|
||||||
|
{
|
||||||
|
"id": "japan",
|
||||||
|
"name": "Nhật Bản",
|
||||||
|
"flag": "🇯🇵",
|
||||||
|
"visaTypes": ["Du lịch", "Công tác", "Thăm thân"],
|
||||||
|
"processingTime": "5-7 ngày",
|
||||||
|
"validityPeriod": "90 ngày",
|
||||||
|
"fee": "1,200,000 VNĐ",
|
||||||
|
"requirements": [
|
||||||
|
"Hộ chiếu còn hạn trên 6 tháng",
|
||||||
|
"Ảnh 4.5x4.5cm",
|
||||||
|
"Chứng minh tài chính",
|
||||||
|
"Lịch trình du lịch",
|
||||||
|
"Đặt chỗ khách sạn"
|
||||||
|
],
|
||||||
|
"description": "Visa Nhật Bản cho phép khám phá đất nước mặt trời mọc"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "korea",
|
||||||
|
"name": "Hàn Quốc",
|
||||||
|
"flag": "🇰🇷",
|
||||||
|
"visaTypes": ["C-3", "C-4", "D-2"],
|
||||||
|
"processingTime": "5-10 ngày",
|
||||||
|
"validityPeriod": "90 ngày",
|
||||||
|
"fee": "1,500,000 VNĐ",
|
||||||
|
"requirements": [
|
||||||
|
"Hộ chiếu còn hạn trên 6 tháng",
|
||||||
|
"Ảnh 3.5x4.5cm",
|
||||||
|
"Chứng minh tài chính",
|
||||||
|
"Lịch trình du lịch",
|
||||||
|
"Bảo hiểm du lịch"
|
||||||
|
],
|
||||||
|
"description": "Visa Hàn Quốc cho phép du lịch và khám phá văn hóa K-pop"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "Châu Đại Dương",
|
||||||
|
"countries": [
|
||||||
|
{
|
||||||
|
"id": "australia",
|
||||||
|
"name": "Úc",
|
||||||
|
"flag": "🇦🇺",
|
||||||
|
"visaTypes": ["Visitor", "Student", "Work"],
|
||||||
|
"processingTime": "15-30 ngày",
|
||||||
|
"validityPeriod": "1 năm",
|
||||||
|
"fee": "4,200,000 VNĐ",
|
||||||
|
"requirements": [
|
||||||
|
"Hộ chiếu còn hạn trên 6 tháng",
|
||||||
|
"Ảnh passport",
|
||||||
|
"Chứng minh tài chính",
|
||||||
|
"Bảo hiểm y tế",
|
||||||
|
"Khám sức khỏe"
|
||||||
|
],
|
||||||
|
"description": "Visa Úc cho phép du lịch và khám phá lục địa kangaroo"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "newzealand",
|
||||||
|
"name": "New Zealand",
|
||||||
|
"flag": "🇳🇿",
|
||||||
|
"visaTypes": ["Visitor", "Student", "Work"],
|
||||||
|
"processingTime": "20-25 ngày",
|
||||||
|
"validityPeriod": "9 tháng",
|
||||||
|
"fee": "3,500,000 VNĐ",
|
||||||
|
"requirements": [
|
||||||
|
"Hộ chiếu còn hạn trên 3 tháng",
|
||||||
|
"Ảnh passport",
|
||||||
|
"Chứng minh tài chính",
|
||||||
|
"Bảo hiểm du lịch",
|
||||||
|
"Lịch trình chi tiết"
|
||||||
|
],
|
||||||
|
"description": "Visa New Zealand cho phép khám phá xứ sở cừu và phong cảnh tuyệt đẹp"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"popularCountries": [
|
||||||
|
{
|
||||||
|
"id": "japan",
|
||||||
|
"name": "Nhật Bản",
|
||||||
|
"flag": "🇯🇵",
|
||||||
|
"reason": "Thủ tục đơn giản, tỷ lệ thành công cao"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "korea",
|
||||||
|
"name": "Hàn Quốc",
|
||||||
|
"flag": "🇰🇷",
|
||||||
|
"reason": "Văn hóa K-pop, ẩm thực hấp dẫn"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "schengen",
|
||||||
|
"name": "Schengen",
|
||||||
|
"flag": "🇪🇺",
|
||||||
|
"reason": "Một visa đi được 26 quốc gia"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "usa",
|
||||||
|
"name": "Mỹ",
|
||||||
|
"flag": "🇺🇸",
|
||||||
|
"reason": "Visa 10 năm, nhiều cơ hội"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
231
app/countries/page.tsx
Normal file
231
app/countries/page.tsx
Normal file
@@ -0,0 +1,231 @@
|
|||||||
|
"use client";
|
||||||
|
|
||||||
|
import { useState } from "react";
|
||||||
|
import countriesData from "./countries.json";
|
||||||
|
|
||||||
|
export default function CountriesPage() {
|
||||||
|
const [selectedRegion, setSelectedRegion] = useState("all");
|
||||||
|
const [selectedCountry, setSelectedCountry] = useState<string | null>(null);
|
||||||
|
|
||||||
|
const filteredRegions =
|
||||||
|
selectedRegion === "all"
|
||||||
|
? countriesData.regions
|
||||||
|
: countriesData.regions.filter(
|
||||||
|
(region) => region.name === selectedRegion,
|
||||||
|
);
|
||||||
|
|
||||||
|
const handleCountryClick = (countryId: string) => {
|
||||||
|
setSelectedCountry(selectedCountry === countryId ? null : countryId);
|
||||||
|
};
|
||||||
|
|
||||||
|
const getCountryById = (id: string) => {
|
||||||
|
for (const region of countriesData.regions) {
|
||||||
|
const country = region.countries.find((c) => c.id === id);
|
||||||
|
if (country) return country;
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
};
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div className="container mx-auto px-4 py-8">
|
||||||
|
<div className="max-w-6xl mx-auto">
|
||||||
|
{/* Header */}
|
||||||
|
<div className="text-center mb-12">
|
||||||
|
<h1 className="text-4xl font-bold text-gray-900 mb-4">
|
||||||
|
{countriesData.title}
|
||||||
|
</h1>
|
||||||
|
<p className="text-xl text-gray-600">{countriesData.subtitle}</p>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
{/* Popular Countries */}
|
||||||
|
<div className="mb-12">
|
||||||
|
<h2 className="text-2xl font-bold text-gray-900 mb-6 text-center">
|
||||||
|
Quốc Gia Phổ Biến
|
||||||
|
</h2>
|
||||||
|
<div className="grid md:grid-cols-4 gap-6">
|
||||||
|
{countriesData.popularCountries.map((country) => (
|
||||||
|
<div
|
||||||
|
key={country.id}
|
||||||
|
className="bg-white rounded-lg shadow-md p-6 text-center hover:shadow-lg transition-shadow cursor-pointer"
|
||||||
|
onClick={() => handleCountryClick(country.id)}
|
||||||
|
>
|
||||||
|
<div className="text-4xl mb-3">{country.flag}</div>
|
||||||
|
<h3 className="font-semibold text-gray-900 mb-2">
|
||||||
|
{country.name}
|
||||||
|
</h3>
|
||||||
|
<p className="text-gray-600 text-sm">{country.reason}</p>
|
||||||
|
</div>
|
||||||
|
))}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
{/* Region Filter */}
|
||||||
|
<div className="mb-8">
|
||||||
|
<div className="flex flex-wrap gap-4 justify-center">
|
||||||
|
<button
|
||||||
|
onClick={() => setSelectedRegion("all")}
|
||||||
|
className={`px-6 py-2 rounded-full transition-colors ${
|
||||||
|
selectedRegion === "all"
|
||||||
|
? "bg-blue-600 text-white"
|
||||||
|
: "bg-gray-100 text-gray-700 hover:bg-gray-200"
|
||||||
|
}`}
|
||||||
|
>
|
||||||
|
Tất cả
|
||||||
|
</button>
|
||||||
|
{countriesData.regions.map((region) => (
|
||||||
|
<button
|
||||||
|
key={region.name}
|
||||||
|
onClick={() => setSelectedRegion(region.name)}
|
||||||
|
className={`px-6 py-2 rounded-full transition-colors ${
|
||||||
|
selectedRegion === region.name
|
||||||
|
? "bg-blue-600 text-white"
|
||||||
|
: "bg-gray-100 text-gray-700 hover:bg-gray-200"
|
||||||
|
}`}
|
||||||
|
>
|
||||||
|
{region.name}
|
||||||
|
</button>
|
||||||
|
))}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
{/* Countries by Region */}
|
||||||
|
<div className="space-y-12">
|
||||||
|
{filteredRegions.map((region) => (
|
||||||
|
<div key={region.name}>
|
||||||
|
<h2 className="text-2xl font-bold text-gray-900 mb-6">
|
||||||
|
{region.name}
|
||||||
|
</h2>
|
||||||
|
<div className="grid md:grid-cols-2 gap-6">
|
||||||
|
{region.countries.map((country) => (
|
||||||
|
<div
|
||||||
|
key={country.id}
|
||||||
|
className="bg-white rounded-lg shadow-md overflow-hidden"
|
||||||
|
>
|
||||||
|
{/* Country Header */}
|
||||||
|
<div
|
||||||
|
className="p-6 cursor-pointer hover:bg-gray-50 transition-colors"
|
||||||
|
onClick={() => handleCountryClick(country.id)}
|
||||||
|
>
|
||||||
|
<div className="flex items-center justify-between">
|
||||||
|
<div className="flex items-center">
|
||||||
|
<span className="text-3xl mr-4">{country.flag}</span>
|
||||||
|
<div>
|
||||||
|
<h3 className="text-xl font-semibold text-gray-900">
|
||||||
|
{country.name}
|
||||||
|
</h3>
|
||||||
|
<p className="text-gray-600">
|
||||||
|
{country.description}
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<span className="text-gray-400">
|
||||||
|
{selectedCountry === country.id ? "▼" : "▶"}
|
||||||
|
</span>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
{/* Quick Info */}
|
||||||
|
<div className="grid grid-cols-2 gap-4 mt-4 text-sm">
|
||||||
|
<div>
|
||||||
|
<span className="text-gray-500">
|
||||||
|
Thời gian xử lý:
|
||||||
|
</span>
|
||||||
|
<div className="font-medium">
|
||||||
|
{country.processingTime}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div>
|
||||||
|
<span className="text-gray-500">Phí visa:</span>
|
||||||
|
<div className="font-medium text-blue-600">
|
||||||
|
{country.fee}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
{/* Detailed Info */}
|
||||||
|
{selectedCountry === country.id && (
|
||||||
|
<div className="border-t bg-gray-50 p-6">
|
||||||
|
<div className="grid md:grid-cols-2 gap-6">
|
||||||
|
{/* Visa Types */}
|
||||||
|
<div>
|
||||||
|
<h4 className="font-semibold text-gray-900 mb-3">
|
||||||
|
Loại visa:
|
||||||
|
</h4>
|
||||||
|
<div className="flex flex-wrap gap-2">
|
||||||
|
{country.visaTypes.map((type, index) => (
|
||||||
|
<span
|
||||||
|
key={index}
|
||||||
|
className="bg-blue-100 text-blue-800 px-2 py-1 rounded text-sm"
|
||||||
|
>
|
||||||
|
{type}
|
||||||
|
</span>
|
||||||
|
))}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
{/* Validity */}
|
||||||
|
<div>
|
||||||
|
<h4 className="font-semibold text-gray-900 mb-3">
|
||||||
|
Thời hạn visa:
|
||||||
|
</h4>
|
||||||
|
<p className="text-gray-700">
|
||||||
|
{country.validityPeriod}
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
{/* Requirements */}
|
||||||
|
<div className="mt-6">
|
||||||
|
<h4 className="font-semibold text-gray-900 mb-3">
|
||||||
|
Yêu cầu hồ sơ:
|
||||||
|
</h4>
|
||||||
|
<ul className="space-y-2">
|
||||||
|
{country.requirements.map((req, index) => (
|
||||||
|
<li
|
||||||
|
key={index}
|
||||||
|
className="flex items-start text-gray-700"
|
||||||
|
>
|
||||||
|
<span className="text-green-500 mr-2 mt-1">
|
||||||
|
✓
|
||||||
|
</span>
|
||||||
|
{req}
|
||||||
|
</li>
|
||||||
|
))}
|
||||||
|
</ul>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
{/* CTA */}
|
||||||
|
<div className="mt-6 flex gap-4">
|
||||||
|
<button className="bg-blue-600 text-white px-6 py-2 rounded-lg hover:bg-blue-700 transition-colors">
|
||||||
|
Tư vấn ngay
|
||||||
|
</button>
|
||||||
|
<button className="bg-gray-100 text-gray-700 px-6 py-2 rounded-lg hover:bg-gray-200 transition-colors">
|
||||||
|
Xem chi tiết
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
|
</div>
|
||||||
|
))}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
))}
|
||||||
|
</div>
|
||||||
|
|
||||||
|
{/* Contact CTA */}
|
||||||
|
<div className="text-center mt-12 bg-blue-50 rounded-lg p-8">
|
||||||
|
<h2 className="text-2xl font-bold text-gray-900 mb-4">
|
||||||
|
Không Tìm Thấy Quốc Gia Bạn Cần?
|
||||||
|
</h2>
|
||||||
|
<p className="text-gray-700 mb-6">
|
||||||
|
Chúng tôi hỗ trợ visa cho hơn 50 quốc gia. Liên hệ để được tư vấn
|
||||||
|
chi tiết
|
||||||
|
</p>
|
||||||
|
<button className="bg-blue-600 text-white px-8 py-3 rounded-lg hover:bg-blue-700 transition-colors">
|
||||||
|
Liên hệ tư vấn
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
}
|
||||||
@@ -1,6 +1,8 @@
|
|||||||
import type { Metadata } from "next";
|
import type { Metadata } from "next";
|
||||||
import { Geist, Geist_Mono } from "next/font/google";
|
import { Geist, Geist_Mono } from "next/font/google";
|
||||||
import "./globals.css";
|
import "./globals.css";
|
||||||
|
import Header from "./components/Header";
|
||||||
|
import Footer from "./components/Footer";
|
||||||
|
|
||||||
const geistSans = Geist({
|
const geistSans = Geist({
|
||||||
variable: "--font-geist-sans",
|
variable: "--font-geist-sans",
|
||||||
@@ -13,8 +15,9 @@ const geistMono = Geist_Mono({
|
|||||||
});
|
});
|
||||||
|
|
||||||
export const metadata: Metadata = {
|
export const metadata: Metadata = {
|
||||||
title: "Create Next App",
|
title: "VisaService - Dịch vụ visa uy tín",
|
||||||
description: "Generated by create next app",
|
description:
|
||||||
|
"Dịch vụ visa chuyên nghiệp với tỷ lệ thành công 99%. Hỗ trợ visa cho hơn 50 quốc gia.",
|
||||||
};
|
};
|
||||||
|
|
||||||
export default function RootLayout({
|
export default function RootLayout({
|
||||||
@@ -23,11 +26,13 @@ export default function RootLayout({
|
|||||||
children: React.ReactNode;
|
children: React.ReactNode;
|
||||||
}>) {
|
}>) {
|
||||||
return (
|
return (
|
||||||
<html lang="en">
|
<html lang="vi">
|
||||||
<body
|
<body
|
||||||
className={`${geistSans.variable} ${geistMono.variable} antialiased`}
|
className={`${geistSans.variable} ${geistMono.variable} antialiased bg-gray-50`}
|
||||||
>
|
>
|
||||||
{children}
|
<Header />
|
||||||
|
<main className="min-h-screen">{children}</main>
|
||||||
|
<Footer />
|
||||||
</body>
|
</body>
|
||||||
</html>
|
</html>
|
||||||
);
|
);
|
||||||
|
|||||||
188
app/pricing/page.tsx
Normal file
188
app/pricing/page.tsx
Normal file
@@ -0,0 +1,188 @@
|
|||||||
|
import pricingData from "./pricing.json";
|
||||||
|
|
||||||
|
export default function PricingPage() {
|
||||||
|
return (
|
||||||
|
<div className="container mx-auto px-4 py-8">
|
||||||
|
<div className="max-w-6xl mx-auto">
|
||||||
|
{/* Header */}
|
||||||
|
<div className="text-center mb-12">
|
||||||
|
<h1 className="text-4xl font-bold text-gray-900 mb-4">
|
||||||
|
{pricingData.title}
|
||||||
|
</h1>
|
||||||
|
<p className="text-xl text-gray-600 mb-4">{pricingData.subtitle}</p>
|
||||||
|
<p className="text-sm text-gray-500 bg-yellow-50 p-3 rounded-lg inline-block">
|
||||||
|
💡 {pricingData.note}
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
{/* Pricing Packages */}
|
||||||
|
<div className="grid md:grid-cols-3 gap-8 mb-16">
|
||||||
|
{pricingData.packages.map((pkg) => (
|
||||||
|
<div
|
||||||
|
key={pkg.id}
|
||||||
|
className={`relative bg-white rounded-lg shadow-lg p-6 ${
|
||||||
|
pkg.popular ? "ring-2 ring-blue-500 transform scale-105" : ""
|
||||||
|
}`}
|
||||||
|
>
|
||||||
|
{/* Popular Badge */}
|
||||||
|
{pkg.popular && (
|
||||||
|
<div className="absolute -top-3 left-1/2 transform -translate-x-1/2">
|
||||||
|
<span className="bg-blue-500 text-white px-4 py-1 rounded-full text-sm font-medium">
|
||||||
|
Phổ biến nhất
|
||||||
|
</span>
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
|
|
||||||
|
{/* Package Header */}
|
||||||
|
<div className="text-center mb-6">
|
||||||
|
<h3 className="text-2xl font-bold text-gray-900 mb-2">
|
||||||
|
{pkg.name}
|
||||||
|
</h3>
|
||||||
|
<p className="text-gray-600 mb-4">{pkg.description}</p>
|
||||||
|
<div className="text-3xl font-bold text-blue-600">
|
||||||
|
{parseInt(pkg.price).toLocaleString()} {pkg.currency}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
{/* Features */}
|
||||||
|
<div className="mb-6">
|
||||||
|
<h4 className="font-semibold text-gray-900 mb-3">Bao gồm:</h4>
|
||||||
|
<ul className="space-y-2">
|
||||||
|
{pkg.features.map((feature, index) => (
|
||||||
|
<li key={index} className="flex items-center text-gray-700">
|
||||||
|
<span className="text-green-500 mr-2">✓</span>
|
||||||
|
{feature}
|
||||||
|
</li>
|
||||||
|
))}
|
||||||
|
</ul>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
{/* Limitations */}
|
||||||
|
{pkg.limitations.length > 0 && (
|
||||||
|
<div className="mb-6">
|
||||||
|
<h4 className="font-semibold text-gray-900 mb-3">Lưu ý:</h4>
|
||||||
|
<ul className="space-y-2">
|
||||||
|
{pkg.limitations.map((limitation, index) => (
|
||||||
|
<li
|
||||||
|
key={index}
|
||||||
|
className="flex items-center text-gray-600 text-sm"
|
||||||
|
>
|
||||||
|
<span className="text-orange-500 mr-2">⚠</span>
|
||||||
|
{limitation}
|
||||||
|
</li>
|
||||||
|
))}
|
||||||
|
</ul>
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
|
|
||||||
|
{/* CTA Button */}
|
||||||
|
<button
|
||||||
|
className={`w-full py-3 px-6 rounded-lg font-medium transition-colors ${
|
||||||
|
pkg.popular
|
||||||
|
? "bg-blue-600 text-white hover:bg-blue-700"
|
||||||
|
: "bg-gray-100 text-gray-900 hover:bg-gray-200"
|
||||||
|
}`}
|
||||||
|
>
|
||||||
|
Chọn gói này
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
))}
|
||||||
|
</div>
|
||||||
|
|
||||||
|
{/* Additional Services */}
|
||||||
|
<div className="mb-16">
|
||||||
|
<h2 className="text-2xl font-bold text-gray-900 mb-8 text-center">
|
||||||
|
Dịch Vụ Bổ Sung
|
||||||
|
</h2>
|
||||||
|
<div className="grid md:grid-cols-2 lg:grid-cols-4 gap-6">
|
||||||
|
{pricingData.additionalServices.map((service, index) => (
|
||||||
|
<div
|
||||||
|
key={index}
|
||||||
|
className="bg-white rounded-lg shadow-md p-6 text-center"
|
||||||
|
>
|
||||||
|
<h3 className="font-semibold text-gray-900 mb-2">
|
||||||
|
{service.name}
|
||||||
|
</h3>
|
||||||
|
<div className="text-2xl font-bold text-blue-600 mb-2">
|
||||||
|
{parseInt(service.price).toLocaleString()} VNĐ
|
||||||
|
</div>
|
||||||
|
<div className="text-sm text-gray-500 mb-3">
|
||||||
|
/{service.unit}
|
||||||
|
</div>
|
||||||
|
<p className="text-gray-700 text-sm">{service.description}</p>
|
||||||
|
</div>
|
||||||
|
))}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
{/* Country Pricing */}
|
||||||
|
<div>
|
||||||
|
<h2 className="text-2xl font-bold text-gray-900 mb-8 text-center">
|
||||||
|
Phí Visa Theo Quốc Gia
|
||||||
|
</h2>
|
||||||
|
<div className="bg-white rounded-lg shadow-lg overflow-hidden">
|
||||||
|
<div className="overflow-x-auto">
|
||||||
|
<table className="w-full">
|
||||||
|
<thead className="bg-gray-50">
|
||||||
|
<tr>
|
||||||
|
<th className="px-6 py-3 text-left text-xs font-medium text-gray-500 uppercase tracking-wider">
|
||||||
|
Quốc gia
|
||||||
|
</th>
|
||||||
|
<th className="px-6 py-3 text-left text-xs font-medium text-gray-500 uppercase tracking-wider">
|
||||||
|
Phí visa
|
||||||
|
</th>
|
||||||
|
<th className="px-6 py-3 text-left text-xs font-medium text-gray-500 uppercase tracking-wider">
|
||||||
|
Thời gian xử lý
|
||||||
|
</th>
|
||||||
|
<th className="px-6 py-3 text-left text-xs font-medium text-gray-500 uppercase tracking-wider">
|
||||||
|
Tỷ lệ thành công
|
||||||
|
</th>
|
||||||
|
</tr>
|
||||||
|
</thead>
|
||||||
|
<tbody className="bg-white divide-y divide-gray-200">
|
||||||
|
{pricingData.countries.map((country, index) => (
|
||||||
|
<tr key={index} className="hover:bg-gray-50">
|
||||||
|
<td className="px-6 py-4 whitespace-nowrap text-sm font-medium text-gray-900">
|
||||||
|
{country.name}
|
||||||
|
</td>
|
||||||
|
<td className="px-6 py-4 whitespace-nowrap text-sm text-gray-900">
|
||||||
|
{parseInt(country.visaFee).toLocaleString()} VNĐ
|
||||||
|
</td>
|
||||||
|
<td className="px-6 py-4 whitespace-nowrap text-sm text-gray-900">
|
||||||
|
{country.processingTime}
|
||||||
|
</td>
|
||||||
|
<td className="px-6 py-4 whitespace-nowrap">
|
||||||
|
<span className="inline-flex items-center px-2.5 py-0.5 rounded-full text-xs font-medium bg-green-100 text-green-800">
|
||||||
|
{country.successRate}
|
||||||
|
</span>
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
))}
|
||||||
|
</tbody>
|
||||||
|
</table>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
{/* Contact CTA */}
|
||||||
|
<div className="text-center mt-12 bg-blue-50 rounded-lg p-8">
|
||||||
|
<h2 className="text-2xl font-bold text-gray-900 mb-4">
|
||||||
|
Cần Tư Vấn Chi Tiết?
|
||||||
|
</h2>
|
||||||
|
<p className="text-gray-700 mb-6">
|
||||||
|
Liên hệ với chúng tôi để được tư vấn miễn phí và nhận báo giá phù
|
||||||
|
hợp nhất
|
||||||
|
</p>
|
||||||
|
<div className="flex flex-col sm:flex-row gap-4 justify-center">
|
||||||
|
<button className="bg-blue-600 text-white px-8 py-3 rounded-lg hover:bg-blue-700 transition-colors">
|
||||||
|
Tư vấn miễn phí
|
||||||
|
</button>
|
||||||
|
<button className="bg-white text-blue-600 border border-blue-600 px-8 py-3 rounded-lg hover:bg-blue-50 transition-colors">
|
||||||
|
Gọi ngay: 1900 1234
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
}
|
||||||
128
app/pricing/pricing.json
Normal file
128
app/pricing/pricing.json
Normal file
@@ -0,0 +1,128 @@
|
|||||||
|
{
|
||||||
|
"title": "Bảng Giá Dịch Vụ",
|
||||||
|
"subtitle": "Giá cả minh bạch, không phí ẩn",
|
||||||
|
"note": "Giá có thể thay đổi tùy theo từng trường hợp cụ thể. Liên hệ để được tư vấn chi tiết.",
|
||||||
|
"packages": [
|
||||||
|
{
|
||||||
|
"id": "basic",
|
||||||
|
"name": "Gói Cơ Bản",
|
||||||
|
"description": "Phù hợp cho khách hàng lần đầu xin visa",
|
||||||
|
"price": "1,500,000",
|
||||||
|
"currency": "VNĐ",
|
||||||
|
"popular": false,
|
||||||
|
"features": [
|
||||||
|
"Tư vấn miễn phí",
|
||||||
|
"Kiểm tra hồ sơ cơ bản",
|
||||||
|
"Hướng dẫn điền form",
|
||||||
|
"Hỗ trợ qua email",
|
||||||
|
"Bảo hành 30 ngày"
|
||||||
|
],
|
||||||
|
"limitations": [
|
||||||
|
"Không bao gồm phí lãnh sự",
|
||||||
|
"Không dịch thuật",
|
||||||
|
"Hỗ trợ trong giờ hành chính"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "premium",
|
||||||
|
"name": "Gói Cao Cấp",
|
||||||
|
"description": "Dịch vụ toàn diện với nhiều ưu đãi",
|
||||||
|
"price": "3,500,000",
|
||||||
|
"currency": "VNĐ",
|
||||||
|
"popular": true,
|
||||||
|
"features": [
|
||||||
|
"Tất cả tính năng gói Cơ Bản",
|
||||||
|
"Dịch thuật chuyên nghiệp",
|
||||||
|
"Kiểm tra hồ sơ chi tiết",
|
||||||
|
"Hỗ trợ 24/7",
|
||||||
|
"Bảo hành 90 ngày",
|
||||||
|
"Tư vấn trực tiếp",
|
||||||
|
"Hỗ trợ booking phỏng vấn"
|
||||||
|
],
|
||||||
|
"limitations": ["Không bao gồm phí lãnh sự"]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "vip",
|
||||||
|
"name": "Gói VIP",
|
||||||
|
"description": "Dịch vụ cao cấp nhất với cam kết thành công",
|
||||||
|
"price": "6,500,000",
|
||||||
|
"currency": "VNĐ",
|
||||||
|
"popular": false,
|
||||||
|
"features": [
|
||||||
|
"Tất cả tính năng gói Cao Cấp",
|
||||||
|
"Cam kết thành công 99%",
|
||||||
|
"Hoàn tiền nếu bị từ chối",
|
||||||
|
"Tư vấn 1-1 với chuyên gia",
|
||||||
|
"Ưu tiên xử lý hồ sơ",
|
||||||
|
"Bảo hành 1 năm",
|
||||||
|
"Hỗ trợ sau khi có visa",
|
||||||
|
"Tặng bảo hiểm du lịch"
|
||||||
|
],
|
||||||
|
"limitations": []
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"additionalServices": [
|
||||||
|
{
|
||||||
|
"name": "Dịch thuật công chứng",
|
||||||
|
"price": "200,000",
|
||||||
|
"unit": "trang",
|
||||||
|
"description": "Dịch thuật và công chứng các loại giấy tờ"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "Chụp ảnh visa",
|
||||||
|
"price": "100,000",
|
||||||
|
"unit": "bộ",
|
||||||
|
"description": "Chụp ảnh theo tiêu chuẩn từng quốc gia"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "Đặt lịch phỏng vấn",
|
||||||
|
"price": "500,000",
|
||||||
|
"unit": "lần",
|
||||||
|
"description": "Hỗ trợ đặt lịch phỏng vấn tại lãnh sự quán"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "Tư vấn phỏng vấn",
|
||||||
|
"price": "800,000",
|
||||||
|
"unit": "buổi",
|
||||||
|
"description": "Luyện tập và tư vấn kỹ năng phỏng vấn"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"countries": [
|
||||||
|
{
|
||||||
|
"name": "Mỹ",
|
||||||
|
"visaFee": "4,600,000",
|
||||||
|
"processingTime": "3-5 tuần",
|
||||||
|
"successRate": "95%"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "Schengen",
|
||||||
|
"visaFee": "2,200,000",
|
||||||
|
"processingTime": "2-3 tuần",
|
||||||
|
"successRate": "98%"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "Anh",
|
||||||
|
"visaFee": "3,800,000",
|
||||||
|
"processingTime": "3-4 tuần",
|
||||||
|
"successRate": "96%"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "Canada",
|
||||||
|
"visaFee": "2,800,000",
|
||||||
|
"processingTime": "4-6 tuần",
|
||||||
|
"successRate": "94%"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "Úc",
|
||||||
|
"visaFee": "4,200,000",
|
||||||
|
"processingTime": "2-4 tuần",
|
||||||
|
"successRate": "97%"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "Nhật Bản",
|
||||||
|
"visaFee": "1,200,000",
|
||||||
|
"processingTime": "1-2 tuần",
|
||||||
|
"successRate": "99%"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
73
app/services/page.tsx
Normal file
73
app/services/page.tsx
Normal file
@@ -0,0 +1,73 @@
|
|||||||
|
import servicesData from "./services.json";
|
||||||
|
|
||||||
|
export default function ServicesPage() {
|
||||||
|
return (
|
||||||
|
<div className="container mx-auto px-4 py-8">
|
||||||
|
<div className="max-w-6xl mx-auto">
|
||||||
|
{/* Header */}
|
||||||
|
<div className="text-center mb-12">
|
||||||
|
<h1 className="text-4xl font-bold text-gray-900 mb-4">
|
||||||
|
{servicesData.title}
|
||||||
|
</h1>
|
||||||
|
<p className="text-xl text-gray-600">{servicesData.subtitle}</p>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
{/* Services Grid */}
|
||||||
|
<div className="grid md:grid-cols-2 lg:grid-cols-2 gap-8">
|
||||||
|
{servicesData.services.map((service) => (
|
||||||
|
<div
|
||||||
|
key={service.id}
|
||||||
|
className="bg-white rounded-lg shadow-lg p-6 hover:shadow-xl transition-shadow"
|
||||||
|
>
|
||||||
|
{/* Service Header */}
|
||||||
|
<div className="flex items-center mb-4">
|
||||||
|
<span className="text-4xl mr-4">{service.icon}</span>
|
||||||
|
<div>
|
||||||
|
<h3 className="text-xl font-semibold text-gray-900">
|
||||||
|
{service.name}
|
||||||
|
</h3>
|
||||||
|
<p className="text-blue-600 font-medium">{service.price}</p>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
{/* Description */}
|
||||||
|
<p className="text-gray-700 mb-6">{service.description}</p>
|
||||||
|
|
||||||
|
{/* Features */}
|
||||||
|
<div className="mb-6">
|
||||||
|
<h4 className="font-semibold text-gray-900 mb-3">Tính năng:</h4>
|
||||||
|
<ul className="space-y-2">
|
||||||
|
{service.features.map((feature, index) => (
|
||||||
|
<li key={index} className="flex items-center text-gray-700">
|
||||||
|
<span className="text-green-500 mr-2">✓</span>
|
||||||
|
{feature}
|
||||||
|
</li>
|
||||||
|
))}
|
||||||
|
</ul>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
{/* CTA Button */}
|
||||||
|
<button className="w-full bg-blue-600 text-white py-2 px-4 rounded-lg hover:bg-blue-700 transition-colors">
|
||||||
|
Tìm Hiểu Thêm
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
))}
|
||||||
|
</div>
|
||||||
|
|
||||||
|
{/* Contact CTA */}
|
||||||
|
<div className="text-center mt-12 bg-gray-50 rounded-lg p-8">
|
||||||
|
<h2 className="text-2xl font-bold text-gray-900 mb-4">
|
||||||
|
Cần Tư Vấn Thêm?
|
||||||
|
</h2>
|
||||||
|
<p className="text-gray-700 mb-6">
|
||||||
|
Liên hệ với chúng tôi để được tư vấn miễn phí và nhận báo giá chi
|
||||||
|
tiết
|
||||||
|
</p>
|
||||||
|
<button className="bg-green-600 text-white px-8 py-3 rounded-lg hover:bg-green-700 transition-colors">
|
||||||
|
Liên Hệ Ngay
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
}
|
||||||
58
app/services/services.json
Normal file
58
app/services/services.json
Normal file
@@ -0,0 +1,58 @@
|
|||||||
|
{
|
||||||
|
"title": "Dịch Vụ Của Chúng Tôi",
|
||||||
|
"subtitle": "Giải pháp toàn diện cho nhu cầu visa và du lịch",
|
||||||
|
"services": [
|
||||||
|
{
|
||||||
|
"id": "visa-consulting",
|
||||||
|
"name": "Tư Vấn Visa",
|
||||||
|
"description": "Tư vấn chuyên sâu về thủ tục visa cho hơn 50 quốc gia",
|
||||||
|
"icon": "🛂",
|
||||||
|
"features": [
|
||||||
|
"Tư vấn miễn phí",
|
||||||
|
"Hỗ trợ 24/7",
|
||||||
|
"Đảm bảo thành công",
|
||||||
|
"Thủ tục nhanh chóng"
|
||||||
|
],
|
||||||
|
"price": "Từ 500,000 VNĐ"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "document-preparation",
|
||||||
|
"name": "Chuẩn Bị Hồ Sơ",
|
||||||
|
"description": "Hỗ trợ chuẩn bị và kiểm tra hồ sơ visa chính xác",
|
||||||
|
"icon": "📋",
|
||||||
|
"features": [
|
||||||
|
"Kiểm tra hồ sơ kỹ lưỡng",
|
||||||
|
"Hướng dẫn chi tiết",
|
||||||
|
"Dịch thuật chuyên nghiệp",
|
||||||
|
"Bảo mật thông tin"
|
||||||
|
],
|
||||||
|
"price": "Từ 300,000 VNĐ"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "travel-booking",
|
||||||
|
"name": "Đặt Vé & Khách Sạn",
|
||||||
|
"description": "Dịch vụ đặt vé máy bay và khách sạn với giá ưu đãi",
|
||||||
|
"icon": "✈️",
|
||||||
|
"features": [
|
||||||
|
"Giá cạnh tranh",
|
||||||
|
"Đặt chỗ linh hoạt",
|
||||||
|
"Hỗ trợ thay đổi",
|
||||||
|
"Bảo hiểm du lịch"
|
||||||
|
],
|
||||||
|
"price": "Phí dịch vụ 2%"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "tour-packages",
|
||||||
|
"name": "Tour Trọn Gói",
|
||||||
|
"description": "Các gói tour du lịch được thiết kế chuyên nghiệp",
|
||||||
|
"icon": "🌍",
|
||||||
|
"features": [
|
||||||
|
"Lịch trình linh hoạt",
|
||||||
|
"Hướng dẫn viên chuyên nghiệp",
|
||||||
|
"Bao gồm visa",
|
||||||
|
"Bảo hiểm toàn diện"
|
||||||
|
],
|
||||||
|
"price": "Từ 15,000,000 VNĐ"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
238
app/visa/page.tsx
Normal file
238
app/visa/page.tsx
Normal file
@@ -0,0 +1,238 @@
|
|||||||
|
"use client";
|
||||||
|
|
||||||
|
import { useState } from "react";
|
||||||
|
import visaData from "./visa.json";
|
||||||
|
|
||||||
|
export default function VisaPage() {
|
||||||
|
const [selectedVisaType, setSelectedVisaType] = useState<string | null>(null);
|
||||||
|
const [openFaq, setOpenFaq] = useState<number | null>(null);
|
||||||
|
|
||||||
|
const handleVisaTypeClick = (visaId: string) => {
|
||||||
|
setSelectedVisaType(selectedVisaType === visaId ? null : visaId);
|
||||||
|
};
|
||||||
|
|
||||||
|
const toggleFaq = (index: number) => {
|
||||||
|
setOpenFaq(openFaq === index ? null : index);
|
||||||
|
};
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div className="container mx-auto px-4 py-8">
|
||||||
|
<div className="max-w-6xl mx-auto">
|
||||||
|
{/* Hero Section */}
|
||||||
|
<div className="text-center mb-16">
|
||||||
|
<h1 className="text-5xl font-bold text-gray-900 mb-4">
|
||||||
|
{visaData.hero.title}
|
||||||
|
</h1>
|
||||||
|
<p className="text-xl text-gray-600 mb-8 max-w-3xl mx-auto">
|
||||||
|
{visaData.hero.description}
|
||||||
|
</p>
|
||||||
|
|
||||||
|
{/* Stats */}
|
||||||
|
<div className="grid md:grid-cols-3 gap-8 max-w-2xl mx-auto">
|
||||||
|
{visaData.hero.stats.map((stat, index) => (
|
||||||
|
<div key={index} className="text-center">
|
||||||
|
<div className="text-4xl font-bold text-blue-600 mb-2">
|
||||||
|
{stat.number}
|
||||||
|
</div>
|
||||||
|
<div className="text-gray-700">{stat.label}</div>
|
||||||
|
</div>
|
||||||
|
))}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
{/* Visa Types */}
|
||||||
|
<div className="mb-16">
|
||||||
|
<h2 className="text-3xl font-bold text-gray-900 mb-8 text-center">
|
||||||
|
Các Loại Visa
|
||||||
|
</h2>
|
||||||
|
<div className="grid md:grid-cols-2 lg:grid-cols-3 gap-6">
|
||||||
|
{visaData.visaTypes.map((visa) => (
|
||||||
|
<div
|
||||||
|
key={visa.id}
|
||||||
|
className="bg-white rounded-lg shadow-md overflow-hidden hover:shadow-lg transition-shadow"
|
||||||
|
>
|
||||||
|
{/* Visa Header */}
|
||||||
|
<div
|
||||||
|
className="p-6 cursor-pointer"
|
||||||
|
onClick={() => handleVisaTypeClick(visa.id)}
|
||||||
|
>
|
||||||
|
<div className="flex items-center mb-4">
|
||||||
|
<span className="text-3xl mr-4">{visa.icon}</span>
|
||||||
|
<h3 className="text-xl font-semibold text-gray-900">
|
||||||
|
{visa.name}
|
||||||
|
</h3>
|
||||||
|
</div>
|
||||||
|
<p className="text-gray-700 mb-4">{visa.description}</p>
|
||||||
|
|
||||||
|
{/* Quick Stats */}
|
||||||
|
<div className="grid grid-cols-2 gap-4 text-sm">
|
||||||
|
<div>
|
||||||
|
<span className="text-gray-500">Thời gian:</span>
|
||||||
|
<div className="font-medium">{visa.processingTime}</div>
|
||||||
|
</div>
|
||||||
|
<div>
|
||||||
|
<span className="text-gray-500">Tỷ lệ thành công:</span>
|
||||||
|
<div className="font-medium text-green-600">
|
||||||
|
{visa.successRate}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
{/* Detailed Info */}
|
||||||
|
{selectedVisaType === visa.id && (
|
||||||
|
<div className="border-t bg-gray-50 p-6">
|
||||||
|
{/* Features */}
|
||||||
|
<div className="mb-6">
|
||||||
|
<h4 className="font-semibold text-gray-900 mb-3">
|
||||||
|
Tính năng:
|
||||||
|
</h4>
|
||||||
|
<ul className="space-y-2">
|
||||||
|
{visa.features.map((feature, index) => (
|
||||||
|
<li
|
||||||
|
key={index}
|
||||||
|
className="flex items-start text-gray-700"
|
||||||
|
>
|
||||||
|
<span className="text-green-500 mr-2 mt-1">✓</span>
|
||||||
|
{feature}
|
||||||
|
</li>
|
||||||
|
))}
|
||||||
|
</ul>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
{/* Popular Countries */}
|
||||||
|
<div className="mb-6">
|
||||||
|
<h4 className="font-semibold text-gray-900 mb-3">
|
||||||
|
Quốc gia phổ biến:
|
||||||
|
</h4>
|
||||||
|
<div className="flex flex-wrap gap-2">
|
||||||
|
{visa.popularCountries.map((country, index) => (
|
||||||
|
<span
|
||||||
|
key={index}
|
||||||
|
className="bg-blue-100 text-blue-800 px-2 py-1 rounded text-sm"
|
||||||
|
>
|
||||||
|
{country}
|
||||||
|
</span>
|
||||||
|
))}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
{/* CTA */}
|
||||||
|
<button className="w-full bg-blue-600 text-white py-2 px-4 rounded-lg hover:bg-blue-700 transition-colors">
|
||||||
|
Tư vấn {visa.name}
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
|
</div>
|
||||||
|
))}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
{/* Process */}
|
||||||
|
<div className="mb-16">
|
||||||
|
<h2 className="text-3xl font-bold text-gray-900 mb-8 text-center">
|
||||||
|
Quy Trình Xin Visa
|
||||||
|
</h2>
|
||||||
|
<div className="relative">
|
||||||
|
{/* Process Line */}
|
||||||
|
<div className="hidden md:block absolute top-1/2 left-0 right-0 h-0.5 bg-blue-200 transform -translate-y-1/2"></div>
|
||||||
|
|
||||||
|
<div className="grid md:grid-cols-5 gap-8">
|
||||||
|
{visaData.process.map((step, index) => (
|
||||||
|
<div key={step.step} className="relative text-center">
|
||||||
|
{/* Step Circle */}
|
||||||
|
<div className="w-16 h-16 bg-blue-600 text-white rounded-full flex items-center justify-center text-2xl mx-auto mb-4 relative z-10">
|
||||||
|
{step.icon}
|
||||||
|
</div>
|
||||||
|
|
||||||
|
{/* Step Number */}
|
||||||
|
<div className="absolute -top-2 -right-2 w-8 h-8 bg-blue-800 text-white rounded-full flex items-center justify-center text-sm font-bold">
|
||||||
|
{step.step}
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<h3 className="font-semibold text-gray-900 mb-2">
|
||||||
|
{step.title}
|
||||||
|
</h3>
|
||||||
|
<p className="text-gray-700 text-sm">{step.description}</p>
|
||||||
|
</div>
|
||||||
|
))}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
{/* Why Choose Us */}
|
||||||
|
<div className="mb-16">
|
||||||
|
<h2 className="text-3xl font-bold text-gray-900 mb-8 text-center">
|
||||||
|
Tại Sao Chọn Chúng Tôi?
|
||||||
|
</h2>
|
||||||
|
<div className="grid md:grid-cols-2 lg:grid-cols-3 gap-6">
|
||||||
|
{visaData.whyChooseUs.map((reason, index) => (
|
||||||
|
<div
|
||||||
|
key={index}
|
||||||
|
className="bg-white rounded-lg shadow-md p-6 text-center"
|
||||||
|
>
|
||||||
|
<div className="text-4xl mb-4">{reason.icon}</div>
|
||||||
|
<h3 className="font-semibold text-gray-900 mb-2">
|
||||||
|
{reason.title}
|
||||||
|
</h3>
|
||||||
|
<p className="text-gray-700">{reason.description}</p>
|
||||||
|
</div>
|
||||||
|
))}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
{/* FAQs */}
|
||||||
|
<div className="mb-16">
|
||||||
|
<h2 className="text-3xl font-bold text-gray-900 mb-8 text-center">
|
||||||
|
Câu Hỏi Thường Gặp
|
||||||
|
</h2>
|
||||||
|
<div className="max-w-3xl mx-auto space-y-4">
|
||||||
|
{visaData.faqs.map((faq, index) => (
|
||||||
|
<div
|
||||||
|
key={index}
|
||||||
|
className="bg-white rounded-lg shadow-md overflow-hidden"
|
||||||
|
>
|
||||||
|
<button
|
||||||
|
className="w-full p-6 text-left hover:bg-gray-50 transition-colors"
|
||||||
|
onClick={() => toggleFaq(index)}
|
||||||
|
>
|
||||||
|
<div className="flex items-center justify-between">
|
||||||
|
<h3 className="font-semibold text-gray-900">
|
||||||
|
{faq.question}
|
||||||
|
</h3>
|
||||||
|
<span className="text-gray-400">
|
||||||
|
{openFaq === index ? "−" : "+"}
|
||||||
|
</span>
|
||||||
|
</div>
|
||||||
|
</button>
|
||||||
|
{openFaq === index && (
|
||||||
|
<div className="px-6 pb-6">
|
||||||
|
<p className="text-gray-700">{faq.answer}</p>
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
|
</div>
|
||||||
|
))}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
{/* CTA Section */}
|
||||||
|
<div className="text-center bg-gradient-to-r from-blue-600 to-purple-600 rounded-lg p-8 text-white">
|
||||||
|
<h2 className="text-3xl font-bold mb-4">
|
||||||
|
Sẵn Sàng Bắt Đầu Hành Trình?
|
||||||
|
</h2>
|
||||||
|
<p className="text-xl mb-6 text-blue-100">
|
||||||
|
Liên hệ ngay để được tư vấn miễn phí và nhận báo giá tốt nhất
|
||||||
|
</p>
|
||||||
|
<div className="flex flex-col sm:flex-row gap-4 justify-center">
|
||||||
|
<button className="bg-white text-blue-600 px-8 py-3 rounded-lg hover:bg-blue-50 transition-colors font-medium">
|
||||||
|
Tư vấn miễn phí
|
||||||
|
</button>
|
||||||
|
<button className="bg-blue-800 text-white px-8 py-3 rounded-lg hover:bg-blue-900 transition-colors font-medium">
|
||||||
|
Gọi ngay: 1900 1234
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
}
|
||||||
206
app/visa/visa.json
Normal file
206
app/visa/visa.json
Normal file
@@ -0,0 +1,206 @@
|
|||||||
|
{
|
||||||
|
"title": "Dịch Vụ Visa",
|
||||||
|
"subtitle": "Hỗ trợ toàn diện cho mọi loại visa",
|
||||||
|
"hero": {
|
||||||
|
"title": "Xin Visa Thành Công 99%",
|
||||||
|
"description": "Với hơn 10 năm kinh nghiệm, chúng tôi cam kết mang đến dịch vụ visa chất lượng cao với tỷ lệ thành công hàng đầu thị trường.",
|
||||||
|
"stats": [
|
||||||
|
{
|
||||||
|
"number": "50+",
|
||||||
|
"label": "Quốc gia"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"number": "99%",
|
||||||
|
"label": "Tỷ lệ thành công"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"number": "10000+",
|
||||||
|
"label": "Khách hàng"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"visaTypes": [
|
||||||
|
{
|
||||||
|
"id": "tourist",
|
||||||
|
"name": "Visa Du Lịch",
|
||||||
|
"icon": "🏖️",
|
||||||
|
"description": "Visa cho mục đích du lịch, nghỉ dưỡng và khám phá",
|
||||||
|
"features": [
|
||||||
|
"Thời hạn linh hoạt từ 30 ngày đến 10 năm",
|
||||||
|
"Hỗ trợ visa đơn và visa nhiều lần",
|
||||||
|
"Tư vấn lịch trình du lịch",
|
||||||
|
"Hỗ trợ đặt vé máy bay và khách sạn"
|
||||||
|
],
|
||||||
|
"popularCountries": ["Nhật Bản", "Hàn Quốc", "Schengen", "Mỹ", "Úc"],
|
||||||
|
"processingTime": "5-30 ngày",
|
||||||
|
"successRate": "99%"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "business",
|
||||||
|
"name": "Visa Công Tác",
|
||||||
|
"icon": "💼",
|
||||||
|
"description": "Visa cho mục đích công tác, hội nghị và kinh doanh",
|
||||||
|
"features": [
|
||||||
|
"Xử lý nhanh chóng cho công việc khẩn cấp",
|
||||||
|
"Hỗ trợ thư mời từ đối tác",
|
||||||
|
"Tư vấn về quy định làm việc",
|
||||||
|
"Visa nhiều lần cho doanh nhân"
|
||||||
|
],
|
||||||
|
"popularCountries": [
|
||||||
|
"Mỹ",
|
||||||
|
"Trung Quốc",
|
||||||
|
"Nhật Bản",
|
||||||
|
"Hàn Quốc",
|
||||||
|
"Singapore"
|
||||||
|
],
|
||||||
|
"processingTime": "3-20 ngày",
|
||||||
|
"successRate": "98%"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "study",
|
||||||
|
"name": "Visa Du Học",
|
||||||
|
"icon": "🎓",
|
||||||
|
"description": "Visa cho mục đích học tập và nghiên cứu",
|
||||||
|
"features": [
|
||||||
|
"Tư vấn chọn trường và ngành học",
|
||||||
|
"Hỗ trợ chuẩn bị hồ sơ học bổng",
|
||||||
|
"Visa dài hạn cho toàn khóa học",
|
||||||
|
"Hỗ trợ gia hạn visa"
|
||||||
|
],
|
||||||
|
"popularCountries": ["Mỹ", "Canada", "Úc", "Anh", "Đức"],
|
||||||
|
"processingTime": "4-8 tuần",
|
||||||
|
"successRate": "96%"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "family",
|
||||||
|
"name": "Visa Thăm Thân",
|
||||||
|
"icon": "👨👩👧👦",
|
||||||
|
"description": "Visa để thăm người thân và gia đình",
|
||||||
|
"features": [
|
||||||
|
"Hỗ trợ chuẩn bị thư mời",
|
||||||
|
"Tư vấn chứng minh mối quan hệ",
|
||||||
|
"Visa ngắn hạn và dài hạn",
|
||||||
|
"Hỗ trợ visa cho cả gia đình"
|
||||||
|
],
|
||||||
|
"popularCountries": ["Mỹ", "Canada", "Úc", "Schengen", "Anh"],
|
||||||
|
"processingTime": "2-6 tuần",
|
||||||
|
"successRate": "97%"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "transit",
|
||||||
|
"name": "Visa Transit",
|
||||||
|
"icon": "✈️",
|
||||||
|
"description": "Visa quá cảnh cho các chuyến bay kết nối",
|
||||||
|
"features": [
|
||||||
|
"Xử lý nhanh trong 24-48 giờ",
|
||||||
|
"Phí dịch vụ ưu đãi",
|
||||||
|
"Hỗ trợ 24/7",
|
||||||
|
"Không cần phỏng vấn"
|
||||||
|
],
|
||||||
|
"popularCountries": ["Trung Quốc", "Hàn Quốc", "Singapore", "UAE"],
|
||||||
|
"processingTime": "1-3 ngày",
|
||||||
|
"successRate": "99%"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "work",
|
||||||
|
"name": "Visa Lao Động",
|
||||||
|
"icon": "🔨",
|
||||||
|
"description": "Visa cho mục đích làm việc dài hạn",
|
||||||
|
"features": [
|
||||||
|
"Tư vấn về thị trường lao động",
|
||||||
|
"Hỗ trợ tìm nhà tuyển dụng",
|
||||||
|
"Visa dài hạn có thể gia hạn",
|
||||||
|
"Hỗ trợ thủ tục định cư"
|
||||||
|
],
|
||||||
|
"popularCountries": ["Canada", "Úc", "Đức", "New Zealand", "Singapore"],
|
||||||
|
"processingTime": "6-12 tuần",
|
||||||
|
"successRate": "94%"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"process": [
|
||||||
|
{
|
||||||
|
"step": 1,
|
||||||
|
"title": "Tư Vấn Miễn Phí",
|
||||||
|
"description": "Chuyên viên tư vấn chi tiết về loại visa phù hợp và yêu cầu hồ sơ",
|
||||||
|
"icon": "💬"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"step": 2,
|
||||||
|
"title": "Chuẩn Bị Hồ Sơ",
|
||||||
|
"description": "Hướng dẫn và hỗ trợ khách hàng chuẩn bị đầy đủ hồ sơ theo yêu cầu",
|
||||||
|
"icon": "📋"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"step": 3,
|
||||||
|
"title": "Kiểm Tra & Nộp Hồ Sơ",
|
||||||
|
"description": "Kiểm tra kỹ lưỡng và nộp hồ sơ tại lãnh sự quán hoặc trung tâm visa",
|
||||||
|
"icon": "✅"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"step": 4,
|
||||||
|
"title": "Theo Dõi Tiến Độ",
|
||||||
|
"description": "Cập nhật tiến độ xử lý và thông báo kết quả cho khách hàng",
|
||||||
|
"icon": "📊"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"step": 5,
|
||||||
|
"title": "Nhận Visa",
|
||||||
|
"description": "Hỗ trợ nhận visa và tư vấn các lưu ý khi xuất nhập cảnh",
|
||||||
|
"icon": "🎉"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"whyChooseUs": [
|
||||||
|
{
|
||||||
|
"title": "Kinh Nghiệm Lâu Năm",
|
||||||
|
"description": "Hơn 10 năm kinh nghiệm trong lĩnh vực dịch vụ visa",
|
||||||
|
"icon": "⭐"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"title": "Tỷ Lệ Thành Công Cao",
|
||||||
|
"description": "Tỷ lệ thành công 99% với hơn 10,000 khách hàng",
|
||||||
|
"icon": "📈"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"title": "Đội Ngũ Chuyên Nghiệp",
|
||||||
|
"description": "Chuyên viên được đào tạo bài bản, cập nhật thường xuyên",
|
||||||
|
"icon": "👥"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"title": "Hỗ Trợ 24/7",
|
||||||
|
"description": "Luôn sẵn sàng hỗ trợ khách hàng mọi lúc, mọi nơi",
|
||||||
|
"icon": "🕒"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"title": "Giá Cả Cạnh Tranh",
|
||||||
|
"description": "Mức phí hợp lý, minh bạch, không phí ẩn",
|
||||||
|
"icon": "💰"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"title": "Bảo Mật Thông Tin",
|
||||||
|
"description": "Cam kết bảo mật tuyệt đối thông tin khách hàng",
|
||||||
|
"icon": "🔒"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"faqs": [
|
||||||
|
{
|
||||||
|
"question": "Tôi có cần phải có kinh nghiệm du lịch trước đó không?",
|
||||||
|
"answer": "Không nhất thiết. Chúng tôi hỗ trợ cả khách hàng lần đầu xin visa. Tuy nhiên, có lịch sử du lịch sẽ giúp tăng tỷ lệ thành công."
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"question": "Thời gian xử lý visa mất bao lâu?",
|
||||||
|
"answer": "Thời gian xử lý tùy thuộc vào từng quốc gia, từ 1-3 ngày (visa transit) đến 6-12 tuần (visa lao động). Chúng tôi sẽ tư vấn cụ thể cho từng trường hợp."
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"question": "Nếu visa bị từ chối thì sao?",
|
||||||
|
"answer": "Chúng tôi có gói VIP cam kết hoàn tiền nếu visa bị từ chối. Đối với các gói khác, chúng tôi sẽ hỗ trợ phân tích lý do và tư vấn nộp lại."
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"question": "Tôi có thể theo dõi tiến độ hồ sơ như thế nào?",
|
||||||
|
"answer": "Chúng tôi sẽ cập nhật tiến độ qua email, SMS và có thể tra cứu online. Khách hàng luôn được thông báo kịp thời về tình trạng hồ sơ."
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"question": "Chi phí dịch vụ có bao gồm phí lãnh sự không?",
|
||||||
|
"answer": "Phí dịch vụ của chúng tôi chưa bao gồm phí lãnh sự quán. Chúng tôi sẽ thông báo rõ ràng tổng chi phí bao gồm cả phí lãnh sự khi tư vấn."
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
0
docker-compose.yml
Normal file
0
docker-compose.yml
Normal file
0
docker-entrypoint.sh
Normal file
0
docker-entrypoint.sh
Normal file
14
pages/[level]/[slug]/index.tsx
Normal file
14
pages/[level]/[slug]/index.tsx
Normal file
@@ -0,0 +1,14 @@
|
|||||||
|
import { useRouter } from "next/router";
|
||||||
|
|
||||||
|
export default function LevelSlugPage() {
|
||||||
|
const router = useRouter();
|
||||||
|
const { level, slug } = router.query;
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div style={{ padding: 20 }}>
|
||||||
|
<h1>Lesson page</h1>
|
||||||
|
<p>Level: {level}</p>
|
||||||
|
<p>Slug: {slug}</p>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
}
|
||||||
13
pages/[level]/index.tsx
Normal file
13
pages/[level]/index.tsx
Normal file
@@ -0,0 +1,13 @@
|
|||||||
|
import { useRouter } from "next/router";
|
||||||
|
|
||||||
|
export default function LevelPage() {
|
||||||
|
const router = useRouter();
|
||||||
|
const { level } = router.query;
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div style={{ padding: 20 }}>
|
||||||
|
<h1>Level page</h1>
|
||||||
|
<p>Level: {level}</p>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
}
|
||||||
6
pages/_app.tsx
Normal file
6
pages/_app.tsx
Normal file
@@ -0,0 +1,6 @@
|
|||||||
|
import type { AppProps } from "next/app";
|
||||||
|
import "../app/globals.css";
|
||||||
|
|
||||||
|
export default function MyApp({ Component, pageProps }: AppProps) {
|
||||||
|
return <Component {...pageProps} />;
|
||||||
|
}
|
||||||
13
pages/_document.tsx
Normal file
13
pages/_document.tsx
Normal file
@@ -0,0 +1,13 @@
|
|||||||
|
import { Html, Head, Main, NextScript } from "next/document";
|
||||||
|
|
||||||
|
export default function Document() {
|
||||||
|
return (
|
||||||
|
<Html lang="en">
|
||||||
|
<Head />
|
||||||
|
<body>
|
||||||
|
<Main />
|
||||||
|
<NextScript />
|
||||||
|
</body>
|
||||||
|
</Html>
|
||||||
|
);
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user