import { useState, useEffect } from 'react'; import { useRouter } from 'next/router'; import Link from 'next/link'; import Image from 'next/image'; import { ShoppingBag, CreditCard, Truck, Check, Plus, X } from 'lucide-react'; import Header from '../components/Header'; import Footer from '../components/Footer'; import cartService, { Cart } from '../services/cart'; import { orderService } from '../services/orders'; import authService from '../services/auth'; import { userService, Address, AddressCreate } from '../services/users'; // Типы для формы оформления заказа interface CheckoutForm { shipping_address_id: number; payment_method: string; notes: string; } // Типы для формы нового адреса interface AddressForm { address_line1: string; address_line2?: string; city: string; state: string; postal_code: string; country: string; is_default: boolean; } export default function CheckoutPage() { const router = useRouter(); const [cart, setCart] = useState(null); const [addresses, setAddresses] = useState([]); const [loading, setLoading] = useState(true); const [submitting, setSubmitting] = useState(false); const [error, setError] = useState(null); const [success, setSuccess] = useState(false); const [orderId, setOrderId] = useState(null); const [showAddressForm, setShowAddressForm] = useState(false); const [addressFormSubmitting, setAddressFormSubmitting] = useState(false); const [addressFormError, setAddressFormError] = useState(null); const [formValidated, setFormValidated] = useState(false); // Состояние формы заказа const [form, setForm] = useState({ shipping_address_id: 0, payment_method: 'credit_card', notes: '' }); // Состояние формы нового адреса const [addressForm, setAddressForm] = useState({ address_line1: '', address_line2: '', city: '', state: '', postal_code: '', country: 'Россия', is_default: false }); // Проверка валидности формы useEffect(() => { // Форма валидна, если выбран адрес доставки и способ оплаты const isValid = form.shipping_address_id > 0 && !!form.payment_method; setFormValidated(isValid); }, [form.shipping_address_id, form.payment_method]); // Загрузка корзины и адресов при монтировании компонента useEffect(() => { const fetchData = async () => { try { // Проверяем, авторизован ли пользователь if (!authService.isAuthenticated()) { router.push('/login?redirect=/checkout'); return; } setLoading(true); // Загружаем корзину const cartData = await cartService.getCart(); setCart(cartData); // Если корзина пуста, перенаправляем на страницу корзины if (cartData.items.length === 0) { router.push('/cart'); return; } try { // Загружаем адреса пользователя const userData = await userService.getCurrentUser(); if (userData.addresses && userData.addresses.length > 0) { setAddresses(userData.addresses); // Устанавливаем адрес по умолчанию, если он есть const defaultAddress = userData.addresses.find(addr => addr.is_default); if (defaultAddress) { setForm(prev => ({ ...prev, shipping_address_id: defaultAddress.id })); } else { setForm(prev => ({ ...prev, shipping_address_id: userData.addresses[0].id })); } } else { // Если у пользователя нет адресов, показываем форму добавления адреса setShowAddressForm(true); } } catch (addressErr) { console.error('Ошибка при загрузке адресов:', addressErr); // Не показываем ошибку пользователю, просто предлагаем добавить адрес setShowAddressForm(true); } setError(null); } catch (err) { console.error('Ошибка при загрузке данных:', err); setError('Не удалось загрузить данные. Пожалуйста, попробуйте позже.'); } finally { setLoading(false); } }; fetchData(); }, [router]); // Обработчик изменения полей формы заказа const handleChange = (e: React.ChangeEvent) => { const { name, value } = e.target; setForm(prev => ({ ...prev, [name]: value })); }; // Обработчик изменения полей формы адреса const handleAddressFormChange = (e: React.ChangeEvent) => { const { name, value } = e.target; // Для чекбокса используем checked, для остальных полей - value if (e.target instanceof HTMLInputElement && e.target.type === 'checkbox') { const checked = e.target.checked; setAddressForm(prev => ({ ...prev, [name]: checked })); } else { setAddressForm(prev => ({ ...prev, [name]: value })); } }; // Обработчик отправки формы заказа const handleSubmit = async (e: React.FormEvent) => { e.preventDefault(); if (!formValidated) { setError('Пожалуйста, заполните все обязательные поля'); // Если нет адресов, показываем форму добавления адреса if (addresses.length === 0) { setShowAddressForm(true); } return; } // Проверяем, что в корзине есть товары if (!cart || cart.items.length === 0) { setError('Ваша корзина пуста. Добавьте товары в корзину, чтобы оформить заказ.'); return; } try { setSubmitting(true); setError(null); console.log('Отправка заказа на сервер:', { shipping_address_id: form.shipping_address_id, payment_method: form.payment_method, notes: form.notes }); // Создаем заказ const orderResponse = await orderService.createOrder({ shipping_address_id: form.shipping_address_id, payment_method: form.payment_method, notes: form.notes }); console.log('Получен ответ от сервера:', orderResponse); // Извлекаем ID заказа из ответа let orderId = null; // Проверяем разные форматы ответа const responseObj = orderResponse as any; if (responseObj && responseObj.id) { orderId = responseObj.id; } else if (responseObj && responseObj.order && responseObj.order.id) { orderId = responseObj.order.id; } // Устанавливаем флаг успешного создания заказа setSuccess(true); setOrderId(orderId); // Очищаем корзину после успешного оформления заказа try { await cartService.clearCart(); } catch (clearErr) { console.error('Ошибка при очистке корзины:', clearErr); // Не показываем эту ошибку пользователю, так как заказ уже создан } // Проверяем, что ID заказа существует и является числом if (orderId && !isNaN(orderId)) { // Перенаправляем на страницу успешного оформления заказа router.push(`/order-success?id=${orderId}`); } else { console.error('Ошибка: ID заказа отсутствует или некорректен', orderResponse); // Перенаправляем на страницу заказов без указания ID router.push('/account/orders'); } } catch (err) { console.error('Ошибка при оформлении заказа:', err); // Выводим подробную информацию об ошибке в консоль if (err.response) { console.error('Статус ответа:', err.response.status); console.error('Данные ответа:', err.response.data); } // Проверяем, есть ли в ответе сервера сообщение об ошибке let errorMessage = 'Не удалось оформить заказ. Пожалуйста, попробуйте позже.'; if (err.response && err.response.data) { if (typeof err.response.data === 'string') { errorMessage = err.response.data; } else if (err.response.data.detail) { if (Array.isArray(err.response.data.detail)) { // Если detail - это массив ошибок валидации const validationErrors = err.response.data.detail.map(error => `${error.loc.join('.')}: ${error.msg}` ).join('; '); errorMessage = `Ошибка валидации: ${validationErrors}`; } else { errorMessage = err.response.data.detail; } } } setError(errorMessage); // Прокручиваем страницу вверх, чтобы показать сообщение об ошибке window.scrollTo({ top: 0, behavior: 'smooth' }); } finally { setSubmitting(false); } }; // Обработчик отправки формы нового адреса const handleAddressSubmit = async (e: React.FormEvent) => { e.preventDefault(); // Предотвращаем стандартное поведение формы // Валидация формы if (!addressForm.address_line1 || !addressForm.city || !addressForm.state || !addressForm.postal_code) { setAddressFormError('Пожалуйста, заполните все обязательные поля'); return; } try { setAddressFormSubmitting(true); setAddressFormError(null); console.log('Отправка адреса на сервер:', addressForm); // Создаем новый адрес const newAddress = await userService.addAddress(addressForm); console.log('Получен ответ от сервера:', newAddress); // Добавляем новый адрес в список и выбираем его setAddresses(prev => [...prev, newAddress]); setForm(prev => ({ ...prev, shipping_address_id: newAddress.id })); // Скрываем форму добавления адреса setShowAddressForm(false); // Сбрасываем форму setAddressForm({ address_line1: '', address_line2: '', city: '', state: '', postal_code: '', country: 'Россия', is_default: false }); } catch (err) { console.error('Ошибка при добавлении адреса:', err); // Выводим подробную информацию об ошибке в консоль if (err.response) { console.error('Ответ сервера:', err.response.status, err.response.data); } // Проверяем, есть ли в ответе сервера сообщение об ошибке let errorMessage = 'Не удалось добавить адрес. Пожалуйста, попробуйте позже.'; if (err.response && err.response.data) { if (typeof err.response.data === 'string') { errorMessage = err.response.data; } else if (err.response.data.detail) { if (Array.isArray(err.response.data.detail)) { // Если detail - это массив ошибок валидации const validationErrors = err.response.data.detail.map(error => `${error.loc.join('.')}: ${error.msg}` ).join('; '); errorMessage = `Ошибка валидации: ${validationErrors}`; } else { errorMessage = err.response.data.detail; } } } setAddressFormError(errorMessage); } finally { setAddressFormSubmitting(false); } }; // Форматирование цены const formatPrice = (price: number): string => { return price.toLocaleString('ru-RU') + ' ₽'; }; // Функция для корректного отображения URL изображения const getImageUrl = (imageUrl: string | undefined): string => { if (!imageUrl) return '/placeholder-image.jpg'; // Проверяем, начинается ли URL с http или https if (imageUrl.startsWith('http://') || imageUrl.startsWith('https://')) { return imageUrl; } // Формируем базовый URL для изображений const baseUrl = process.env.NEXT_PUBLIC_API_URL || 'http://127.0.0.1:8000/api'; const apiBaseUrl = baseUrl.replace(/\/api$/, ''); // Убираем '/api' в конце, если есть // Если URL начинается с /, добавляем базовый URL API if (imageUrl.startsWith('/')) { return `${apiBaseUrl}${imageUrl}`; } // В остальных случаях добавляем базовый URL API и / return `${apiBaseUrl}/${imageUrl}`; }; // Форматирование адреса для отображения const formatAddress = (address: Address): string => { let formattedAddress = address.address_line1; if (address.address_line2) { formattedAddress += `, ${address.address_line2}`; } return `${formattedAddress}, ${address.city}, ${address.state}, ${address.postal_code}, ${address.country}`; }; return (

Оформление заказа

{loading ? (
) : error && !success ? (
{error}
) : success ? (

Заказ успешно оформлен!

Номер вашего заказа: {orderId}

Вы будете перенаправлены на страницу заказа через несколько секунд...

) : cart ? (
{/* Форма оформления заказа */}
{/* Адрес доставки */}

Адрес доставки

{!showAddressForm && ( )}
{showAddressForm ? (

Новый адрес доставки

{addressFormError && (
{addressFormError}
)}
) : null} {addresses.length > 0 ? (
{addresses.map(address => (
))}
) : !showAddressForm ? (

У вас еще нет сохраненных адресов

) : null}
{/* Способ оплаты */}

Способ оплаты

{/* Комментарий к заказу */}

Комментарий к заказу