From b0aef18c387d7351f0e4d5739d0f0566d67d0d2e Mon Sep 17 00:00:00 2001 From: Timofey Date: Thu, 22 May 2025 13:52:02 +0300 Subject: [PATCH] create sender fix --- backend/api/account/client/serializers.py | 65 ++++++++++--------- backend/api/account/client/views.py | 16 +++-- .../(urls)/account/create-as-sender/page.tsx | 58 ++++++++++++++--- frontend/app/api/account/sender/route.ts | 24 +++---- frontend/app/hooks/useForm.ts | 8 +++ frontend/app/types/index.ts | 1 + 6 files changed, 115 insertions(+), 57 deletions(-) diff --git a/backend/api/account/client/serializers.py b/backend/api/account/client/serializers.py index 2369020..7dd1854 100644 --- a/backend/api/account/client/serializers.py +++ b/backend/api/account/client/serializers.py @@ -105,7 +105,7 @@ class CreateRouteSerializer(serializers.ModelSerializer): arrival = serializers.DateTimeField(source='arrival_DT') transport = serializers.ChoiceField(choices=type_transport_choices, source='type_transport') email_notification = serializers.BooleanField(source='receive_msg_by_email') - contact_number = serializers.CharField(write_only=True) + phone_number = serializers.CharField(write_only=True) owner_type = serializers.ChoiceField(choices=[('sender', 'Отправитель'), ('deliverer', 'Перевозчик')]) class Meta: @@ -156,14 +156,14 @@ class CreateRouteSerializer(serializers.ModelSerializer): # проверяем существование городов в указанных странах try: - City.objects.get(name=data['city_from'], country=country_from) + city_from = City.objects.get(name=data['city_from'], country=country_from) except City.DoesNotExist: raise serializers.ValidationError({ "city_from": f"Город '{data['city_from']}' не найден в стране {country_from}" }) try: - City.objects.get(name=data['city_to'], country=country_to) + city_to = City.objects.get(name=data['city_to'], country=country_to) except City.DoesNotExist: raise serializers.ValidationError({ "city_to": f"Город '{data['city_to']}' не найден в стране {country_to}" @@ -172,33 +172,36 @@ class CreateRouteSerializer(serializers.ModelSerializer): return data def create(self, validated_data): - # получаем города и страны - country_from = Country.objects.get(international_name=validated_data.pop('country_from')) - country_to = Country.objects.get(international_name=validated_data.pop('country_to')) - - from_city = City.objects.get(name=validated_data.pop('city_from'), country=country_from) - to_city = City.objects.get(name=validated_data.pop('city_to'), country=country_to) - - # обновляем номер телефона в профиле пользователя - phone_number = validated_data.pop('phone_number') - user_profile = get_object_or_404(UserProfile, user=self.context['request'].user) - - # проверяем, не используется ли этот номер другим пользователем - if UserProfile.objects.filter(phone_number=phone_number).exclude(user=self.context['request'].user).exists(): - raise serializers.ValidationError({ - "phone_number": "Этот номер телефона уже используется другим пользователем" - }) + try: + # получаем города и страны + country_from = Country.objects.get(international_name=validated_data.pop('country_from')) + country_to = Country.objects.get(international_name=validated_data.pop('country_to')) - user_profile.phone_number = phone_number - user_profile.save() - - # создаем маршрут - route = Route.objects.create( - from_city=from_city, - to_city=to_city, - owner=self.context['request'].user, - **validated_data # owner_type приходит с фронта - ) - - return route + from_city = City.objects.get(name=validated_data.pop('city_from'), country=country_from) + to_city = City.objects.get(name=validated_data.pop('city_to'), country=country_to) + + # обновляем номер телефона в профиле пользователя + phone_number = validated_data.pop('phone_number') # Удаляем из validated_data + user_profile = get_object_or_404(UserProfile, user=self.context['request'].user) + + # проверяем, не используется ли этот номер другим пользователем + if UserProfile.objects.filter(phone_number=phone_number).exclude(user=self.context['request'].user).exists(): + raise serializers.ValidationError({ + "phone_number": "Этот номер телефона уже используется другим пользователем" + }) + + user_profile.phone_number = phone_number + user_profile.save() + + # создаем маршрут + route = Route.objects.create( + from_city=from_city, + to_city=to_city, + owner=self.context['request'].user, + **validated_data + ) + + return route + except Exception as e: + raise \ No newline at end of file diff --git a/backend/api/account/client/views.py b/backend/api/account/client/views.py index 843efc5..5439caf 100644 --- a/backend/api/account/client/views.py +++ b/backend/api/account/client/views.py @@ -97,10 +97,18 @@ class AccountActionsView(ViewSet): @action(detail=False, methods=['post']) @handle_exceptions def create_route(self, request): - serializer = CreateRouteSerializer(data=request.data) - serializer.is_valid(raise_exception=True) - serializer.save() - return Response(serializer.data, status=status.HTTP_201_CREATED) + print("[DEBUG] Входящие данные в create_route view:", request.data) + try: + serializer = CreateRouteSerializer(data=request.data, context={'request': request}) + if not serializer.is_valid(): + print("[DEBUG] Ошибки валидации:", serializer.errors) + return Response(serializer.errors, status=status.HTTP_400_BAD_REQUEST) + route = serializer.save() + print("[DEBUG] Маршрут успешно создан в view:", route.id) + return Response(serializer.data, status=status.HTTP_201_CREATED) + except Exception as e: + print("[DEBUG] Необработанная ошибка в create_route:", str(e)) + raise class CityView(ViewSet): diff --git a/frontend/app/(urls)/account/create-as-sender/page.tsx b/frontend/app/(urls)/account/create-as-sender/page.tsx index e9bdb44..0ef5c93 100644 --- a/frontend/app/(urls)/account/create-as-sender/page.tsx +++ b/frontend/app/(urls)/account/create-as-sender/page.tsx @@ -47,12 +47,13 @@ const validationRules = { minLength: 11, pattern: /^\+?[0-9]{11,}$/, }, - comment: { required: false, minLength: 10 }, + comment: { required: false }, email_notification: { required: false }, } const SenderPage = () => { const { user, setUser } = useUserStore() + const today = formatDateToHTML(new Date()) const initialValues: SenderPageProps = { @@ -73,38 +74,75 @@ const SenderPage = () => { const cargoOptions: SelectOption[] = cargo_types.map((type, index) => ({ id: index + 1, - label: cargo_type_translations[type], value: type, + label: cargo_type_translations[type], })) const transportOptions: SelectOption[] = transport_types.map((type, index) => ({ id: index + 1, - label: transport_translations[type], value: type, + label: transport_translations[type], })) - const { values, handleChange, handleSubmit } = useForm( + const { values, handleChange, handleSubmit, resetField } = useForm( initialValues, validationRules, async values => { try { + // находим выбранные опции + const selectedTransport = transportOptions.find( + opt => opt.id.toString() === values.transport + ) + const selectedCargoType = cargoOptions.find(opt => opt.id.toString() === values.cargo_type) + + if (!selectedTransport || !selectedCargoType) { + throw new Error('Некорректный тип транспорта или груза') + } + + // подготавливаем данные для отправки + const requestData = { + ...values, + owner_type: 'sender', + transport: selectedTransport.value, + cargo_type: selectedCargoType.value, + phone_number: values.phone_number, + } + const response = await fetch('/api/account/sender', { - method: 'PATCH', + method: 'POST', headers: { 'Content-Type': 'application/json', }, - body: JSON.stringify(values), + body: JSON.stringify(requestData), }) if (!response.ok) { const error = await response.json() - throw new Error(error.error || 'Ошибка при обновлении данных') + console.error('Ошибка от сервера:', error) + throw new Error(error.error || 'Ошибка при создании маршрута') } - const result = await response.json() - setUser(result.user) - showToast({ type: 'success', message: 'Данные успешно обновлены!' }) + // Обновляем номер телефона в сторе, если он изменился + if (user && user.phone_number !== values.phone_number) { + setUser({ + ...user, + phone_number: values.phone_number, + }) + } + + showToast({ + type: 'success', + message: 'Маршрут успешно создан!', + }) + + // сбрасываем все поля формы кроме телефона + Object.keys(initialValues).forEach(field => { + if (field !== 'phone_number') { + resetField(field) + } + }) } catch (error) { + console.error('Ошибка:', error) showToast({ type: 'error', message: error instanceof Error ? error.message : 'Ой, что то пошло не так..', diff --git a/frontend/app/api/account/sender/route.ts b/frontend/app/api/account/sender/route.ts index 0bf2bc5..a3ae9a4 100644 --- a/frontend/app/api/account/sender/route.ts +++ b/frontend/app/api/account/sender/route.ts @@ -34,18 +34,18 @@ export async function POST(req: NextRequest) { Authorization: `Bearer ${session.accessToken}`, }, body: JSON.stringify({ - owner_type: 'sender', - transport, - country_from, - city_from, - country_to, - city_to, - cargo_type, - departure, - arrival, - phone_number, - comment, - email_notification, + owner_type: data.owner_type, + transport: data.transport, + country_from: data.country_from, + city_from: data.city_from, + country_to: data.country_to, + city_to: data.city_to, + cargo_type: data.cargo_type, + departure: data.departure, + arrival: data.arrival, + phone_number: data.phone_number, + comment: data.comment || '', + email_notification: data.email_notification, }), }) diff --git a/frontend/app/hooks/useForm.ts b/frontend/app/hooks/useForm.ts index e9cdc15..83aaed1 100644 --- a/frontend/app/hooks/useForm.ts +++ b/frontend/app/hooks/useForm.ts @@ -68,6 +68,14 @@ export function useForm>( phone_number: 'Номер телефона', password: 'Пароль', privacy_policy: 'Политика конфиденциальности', + arrival: 'Дата прибытия', + departure: 'Дата отправления', + cargo_type: 'Тип груза', + city_from: 'Город отправления', + country_to: 'Страна назначения', + city_to: 'Город назначения', + country_from_id: 'Страна отправления', + country_to_id: 'Страна отправления', } const validate = () => { diff --git a/frontend/app/types/index.ts b/frontend/app/types/index.ts index a9262c2..b24f9ce 100644 --- a/frontend/app/types/index.ts +++ b/frontend/app/types/index.ts @@ -171,6 +171,7 @@ export interface Route { export interface SelectOption { id: number label: string + value: string | CargoType | TransportType } export interface MultiSelectProps {