dynamic routes for main page
This commit is contained in:
@@ -1,9 +1,7 @@
|
||||
from rest_framework import serializers
|
||||
from routes.models import Route
|
||||
from sitemanagement.models import FAQ, News
|
||||
from django.conf import settings
|
||||
import pytz
|
||||
from routes.constants.routeChoices import cargo_type_choices, type_transport_choices
|
||||
from routes.models import Country
|
||||
from api.account.client.serializers import RouteSerializer
|
||||
|
||||
class FAQMainSerializer(serializers.ModelSerializer):
|
||||
class Meta:
|
||||
@@ -37,4 +35,88 @@ class TelegramSerializer(serializers.Serializer):
|
||||
|
||||
def create(self, validated_data):
|
||||
return type('TelegramMessage', (), validated_data)
|
||||
|
||||
|
||||
|
||||
class HomePageRouteSerializer(RouteSerializer):
|
||||
username = serializers.SerializerMethodField()
|
||||
userImg = serializers.SerializerMethodField()
|
||||
start_point = serializers.SerializerMethodField()
|
||||
country_from = serializers.SerializerMethodField()
|
||||
country_from_icon = serializers.SerializerMethodField()
|
||||
country_from_code = serializers.SerializerMethodField()
|
||||
end_point = serializers.SerializerMethodField()
|
||||
country_to = serializers.SerializerMethodField()
|
||||
country_to_icon = serializers.SerializerMethodField()
|
||||
country_to_code = serializers.SerializerMethodField()
|
||||
cargo_type = serializers.SerializerMethodField()
|
||||
user_request = serializers.SerializerMethodField()
|
||||
user_comment = serializers.CharField(source='comment')
|
||||
moving_type = serializers.SerializerMethodField()
|
||||
estimated_date = serializers.SerializerMethodField()
|
||||
day_out = serializers.DateTimeField(source='departure_DT')
|
||||
day_in = serializers.DateTimeField(source='arrival_DT')
|
||||
|
||||
def get_username(self, obj):
|
||||
return obj.owner.first_name if obj.owner else None
|
||||
|
||||
def get_userImg(self, obj):
|
||||
try:
|
||||
if obj.owner and hasattr(obj.owner, 'userprofile') and obj.owner.userprofile.image:
|
||||
return obj.owner.userprofile.image.url
|
||||
return None
|
||||
except Exception as e:
|
||||
print(f"Error in get_userImg: {e}")
|
||||
return None
|
||||
|
||||
def get_start_point(self, obj):
|
||||
return self.get_from_city_name(obj)
|
||||
|
||||
def get_country_from(self, obj):
|
||||
return self.get_from_country_name(obj)
|
||||
|
||||
def get_country_from_icon(self, obj):
|
||||
country = self.get_from_country_name(obj)
|
||||
if not country:
|
||||
return None
|
||||
try:
|
||||
country_obj = Country.objects.get(international_name=country)
|
||||
return country_obj.flag_img_url
|
||||
except Country.DoesNotExist:
|
||||
return None
|
||||
|
||||
def get_country_from_code(self, obj):
|
||||
country = self.get_from_country_name(obj)
|
||||
return country[:3].upper() if country else None
|
||||
|
||||
def get_end_point(self, obj):
|
||||
return self.get_to_city_name(obj)
|
||||
|
||||
def get_country_to(self, obj):
|
||||
return self.get_to_country_name(obj)
|
||||
|
||||
def get_country_to_icon(self, obj):
|
||||
country = self.get_to_country_name(obj)
|
||||
if not country:
|
||||
return None
|
||||
try:
|
||||
country_obj = Country.objects.get(international_name=country)
|
||||
return country_obj.flag_img_url
|
||||
except Country.DoesNotExist:
|
||||
print(f"Country not found: {country}")
|
||||
return None
|
||||
|
||||
def get_country_to_code(self, obj):
|
||||
country = self.get_to_country_name(obj)
|
||||
return country[:3].upper() if country else None
|
||||
|
||||
def get_cargo_type(self, obj):
|
||||
return self.get_formatted_cargo_type(obj)
|
||||
|
||||
def get_user_request(self, obj):
|
||||
return 'Нужен перевозчик' if obj.owner_type == 'customer' else 'Могу перевезти'
|
||||
|
||||
def get_moving_type(self, obj):
|
||||
return self.get_formatted_transport(obj)
|
||||
|
||||
def get_estimated_date(self, obj):
|
||||
return obj.arrival_DT
|
||||
@@ -4,12 +4,10 @@ from rest_framework import status
|
||||
from rest_framework.views import APIView
|
||||
from rest_framework.response import Response
|
||||
from api.utils.decorators import handle_exceptions
|
||||
from django.db.models import Q
|
||||
from routes.models import Route
|
||||
from routes.constants.routeChoices import owner_type_choices
|
||||
|
||||
from api.main.serializers import FAQMainSerializer, NewsMainSerializer, TelegramSerializer
|
||||
from api.account.client.serializers import RouteSerializer
|
||||
from api.main.serializers import FAQMainSerializer, NewsMainSerializer, TelegramSerializer, HomePageRouteSerializer
|
||||
from sitemanagement.models import FAQ, News
|
||||
|
||||
class FAQView(APIView):
|
||||
@@ -39,16 +37,20 @@ class NewsView(APIView):
|
||||
class LatestRoutesView(APIView):
|
||||
@handle_exceptions
|
||||
def get(self, request):
|
||||
"""Получаем последние 5 маршрутов для каждого типа owner_type"""
|
||||
"""Получаем последние маршруты"""
|
||||
|
||||
latest_routes = {}
|
||||
routes = []
|
||||
owner_types = dict(owner_type_choices).keys()
|
||||
|
||||
for owner_type in owner_types:
|
||||
routes = Route.objects.filter(owner_type=owner_type).order_by('-id')[:5]
|
||||
latest_routes[owner_type] = RouteSerializer(routes, many=True).data
|
||||
routes.extend(
|
||||
HomePageRouteSerializer(
|
||||
Route.objects.filter(owner_type=owner_type).order_by('-id')[:5],
|
||||
many=True
|
||||
).data
|
||||
)
|
||||
|
||||
return Response(latest_routes, status=status.HTTP_200_OK)
|
||||
return Response(routes, status=status.HTTP_200_OK)
|
||||
|
||||
class TelegramMessageView(APIView):
|
||||
@handle_exceptions
|
||||
|
||||
@@ -1,42 +1,43 @@
|
||||
import React from 'react'
|
||||
import Image from 'next/image'
|
||||
import Button from '@/components/ui/Button'
|
||||
import { SearchCardProps } from '@/app/types/index'
|
||||
import { SearchCardProps } from '@/app/types'
|
||||
import noPhoto from '../../../../public/images/noPhoto.png'
|
||||
|
||||
const SearchCard = ({
|
||||
id,
|
||||
username,
|
||||
owner_type,
|
||||
from_city_name,
|
||||
from_country_name,
|
||||
to_city_name,
|
||||
to_country_name,
|
||||
formatted_cargo_type,
|
||||
formatted_transport,
|
||||
type_transport,
|
||||
userImg,
|
||||
start_point,
|
||||
country_from,
|
||||
comment,
|
||||
formatted_departure,
|
||||
formatted_arrival,
|
||||
country_from_icon,
|
||||
country_from_code,
|
||||
end_point,
|
||||
country_to,
|
||||
country_to_icon,
|
||||
country_to_code,
|
||||
cargo_type,
|
||||
user_request,
|
||||
user_comment,
|
||||
moving_type,
|
||||
estimated_date,
|
||||
day_out,
|
||||
day_in,
|
||||
}: SearchCardProps) => {
|
||||
const getUserRequestStyles = () => {
|
||||
if (user_request === 'Нужен перевозчик') {
|
||||
if (owner_type === 'customer') {
|
||||
return 'text-[#065bff]'
|
||||
}
|
||||
return 'text-[#45c226]'
|
||||
}
|
||||
|
||||
const setMovingTypeIcon = () => {
|
||||
if (moving_type === 'Авиатранспорт') {
|
||||
if (type_transport === 'air') {
|
||||
return '/images/airplane.png'
|
||||
}
|
||||
return '/images/car.png'
|
||||
}
|
||||
|
||||
const userRequest = owner_type === 'customer' ? 'Нужен перевозчик' : 'Могу перевезти'
|
||||
|
||||
return (
|
||||
<>
|
||||
{/* десктоп */}
|
||||
@@ -46,21 +47,22 @@ const SearchCard = ({
|
||||
<div className="flex items-center gap-5">
|
||||
<div className="flex h-16 w-16 items-center justify-center rounded-full bg-gray-200">
|
||||
<Image
|
||||
src={userImg}
|
||||
alt={username}
|
||||
src={userImg || noPhoto}
|
||||
alt={`User ${username}`}
|
||||
width={52}
|
||||
height={52}
|
||||
className="rounded-full object-cover"
|
||||
className="aspect-square w-full rounded-full object-cover md:w-[84px]"
|
||||
/>
|
||||
</div>
|
||||
<div className="flex items-center gap-3">
|
||||
<div className="text-base font-semibold">{username}</div>
|
||||
<div className="text-gray-500">|</div>
|
||||
<div className={`text-base font-semibold ${getUserRequestStyles()}`}>
|
||||
{user_request}
|
||||
{userRequest}
|
||||
</div>
|
||||
<div className="ml-1">
|
||||
Тип посылки: <span className="text-orange ml-1 font-semibold">{cargo_type}</span>
|
||||
Тип посылки:{' '}
|
||||
<span className="text-orange ml-1 font-semibold">{formatted_cargo_type}</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
@@ -72,30 +74,37 @@ const SearchCard = ({
|
||||
|
||||
<div className="rounded-lg bg-[#f8f8f8] p-5">
|
||||
<div className="flex items-baseline gap-2">
|
||||
<span className="text-gray-600">{user_comment}</span>
|
||||
<span className="text-gray-600">{comment}</span>
|
||||
</div>
|
||||
</div>
|
||||
<div className="flex justify-end pt-2 text-sm text-gray-500">Объявление № {id}</div>
|
||||
|
||||
<div className="mt-6 flex items-center justify-between">
|
||||
<div className="flex flex-col">
|
||||
{user_request === 'Нужен перевозчик' ? (
|
||||
{userRequest === 'Нужен перевозчик' ? (
|
||||
<span className="text-gray-500">Забрать из:</span>
|
||||
) : (
|
||||
<span className="text-gray-500">Выезжаю из:</span>
|
||||
)}
|
||||
<div className="flex flex-col">
|
||||
<div className="flex items-center">
|
||||
<Image src={country_from_icon} width={26} height={13} alt={country_from_code} />
|
||||
<span className="pr-2 pl-1 text-gray-400">{country_from_code}</span>
|
||||
<Image
|
||||
src={country_from_icon}
|
||||
width={26}
|
||||
height={13}
|
||||
alt={from_country_name.substring(0, 3)}
|
||||
/>
|
||||
<span className="pr-2 pl-1 text-gray-400">
|
||||
{from_country_name.substring(0, 3).toUpperCase()}
|
||||
</span>
|
||||
<span className="text-base font-semibold">
|
||||
{start_point} / {country_from}
|
||||
{from_city_name} / {from_country_name}
|
||||
</span>
|
||||
</div>
|
||||
{user_request === 'Могу перевезти' && (
|
||||
{userRequest === 'Могу перевезти' && (
|
||||
<div className="mt-1 text-sm text-gray-500">
|
||||
<span className="text-sm font-normal">Отправление:</span>{' '}
|
||||
<span className="text-sm font-semibold">{day_out?.toLocaleDateString()}</span>
|
||||
<span className="text-sm font-semibold">{formatted_departure}</span>
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
@@ -103,7 +112,7 @@ const SearchCard = ({
|
||||
|
||||
<div className="text-center">
|
||||
<div className="flex items-center justify-center gap-2">
|
||||
<span className="text-base font-semibold">{moving_type}</span>
|
||||
<span className="text-base font-semibold">{formatted_transport}</span>
|
||||
<Image
|
||||
src={setMovingTypeIcon()}
|
||||
width={15}
|
||||
@@ -124,18 +133,16 @@ const SearchCard = ({
|
||||
<div className="relative z-10 h-5 w-5 rounded-full border-3 border-[#45c226] bg-white" />
|
||||
</div>
|
||||
|
||||
{user_request === 'Нужен перевозчик' && (
|
||||
{userRequest === 'Нужен перевозчик' && (
|
||||
<div className="text-sm text-gray-500">
|
||||
<span className="text-sm font-normal">Дата доставки:</span>{' '}
|
||||
<span className="text-sm font-semibold">
|
||||
{estimated_date.toLocaleDateString()}
|
||||
</span>
|
||||
<span className="text-sm font-semibold">{formatted_arrival}</span>
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
|
||||
<div className="-mb-[14px] flex flex-col">
|
||||
{user_request === 'Нужен перевозчик' ? (
|
||||
{userRequest === 'Нужен перевозчик' ? (
|
||||
<div className="text-base text-gray-500">Доставить в:</div>
|
||||
) : (
|
||||
<div className="text-base text-gray-500">Прибываю в:</div>
|
||||
@@ -143,16 +150,23 @@ const SearchCard = ({
|
||||
|
||||
<div className="flex flex-col">
|
||||
<div className="flex items-center">
|
||||
<Image src={country_to_icon} width={26} height={13} alt={country_to_code} />
|
||||
<span className="pr-2 pl-1 text-gray-400">{country_to_code}</span>
|
||||
<Image
|
||||
src={country_to_icon}
|
||||
width={26}
|
||||
height={13}
|
||||
alt={to_country_name.substring(0, 3)}
|
||||
/>
|
||||
<span className="pr-2 pl-1 text-gray-400">
|
||||
{to_country_name.substring(0, 3).toUpperCase()}
|
||||
</span>
|
||||
<span className="text-base font-semibold">
|
||||
{end_point} / {country_to}
|
||||
{to_city_name} / {to_country_name}
|
||||
</span>
|
||||
</div>
|
||||
{user_request === 'Могу перевезти' && (
|
||||
{userRequest === 'Могу перевезти' && (
|
||||
<div className="text-sm text-gray-500">
|
||||
<span className="text-sm font-normal">Прибытие:</span>{' '}
|
||||
<span className="text-sm font-semibold">{day_in?.toLocaleDateString()}</span>
|
||||
<span className="text-sm font-semibold">{formatted_arrival}</span>
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
@@ -165,28 +179,26 @@ const SearchCard = ({
|
||||
<div className="block sm:hidden">
|
||||
<div className="my-4 w-full rounded-xl bg-white p-4 shadow-lg">
|
||||
<div className="flex items-center justify-between">
|
||||
<div className={`text-sm font-semibold ${getUserRequestStyles()}`}>{user_request}</div>
|
||||
<div className={`text-sm font-semibold ${getUserRequestStyles()}`}>{userRequest}</div>
|
||||
<div className="text-sm font-semibold">
|
||||
Тип посылки: <span className="text-orange">{cargo_type}</span>
|
||||
Тип посылки: <span className="text-orange">{formatted_cargo_type}</span>
|
||||
</div>
|
||||
</div>
|
||||
<div className="mt-5 mb-2 flex flex-row items-center justify-between gap-3">
|
||||
<div className="flex h-16 w-16 min-w-[64px] shrink-0 items-center justify-center rounded-full bg-gray-200">
|
||||
<Image
|
||||
src={userImg}
|
||||
alt={username}
|
||||
src={noPhoto}
|
||||
alt={`User ${username}`}
|
||||
width={52}
|
||||
height={52}
|
||||
className="aspect-square h-[52px] w-[52px] rounded-full object-cover"
|
||||
/>
|
||||
</div>
|
||||
<div className="flex-1 rounded-lg bg-[#f8f8f8] p-4 text-sm font-normal">
|
||||
{user_comment}
|
||||
</div>
|
||||
<div className="flex-1 rounded-lg bg-[#f8f8f8] p-4 text-sm font-normal">{comment}</div>
|
||||
</div>
|
||||
<div className="flex justify-end text-xs text-gray-500">Объявление № {id}</div>
|
||||
|
||||
{user_request === 'Нужен перевозчик' ? (
|
||||
{userRequest === 'Нужен перевозчик' ? (
|
||||
<span className="pl-7 text-sm text-gray-500">Забрать из:</span>
|
||||
) : (
|
||||
<span className="pl-7 text-sm text-gray-500">Выезжаю из:</span>
|
||||
@@ -207,16 +219,23 @@ const SearchCard = ({
|
||||
</div>
|
||||
<div className="flex flex-1 flex-col justify-between">
|
||||
<div className="-mt-[14px] flex items-center">
|
||||
<Image src={country_from_icon} width={26} height={13} alt={country_from_code} />
|
||||
<span className="pr-2 pl-1 text-sm text-gray-400">{country_from_code}</span>
|
||||
<Image
|
||||
src={`/images/flags/${from_country_name.toLowerCase()}.png`}
|
||||
width={26}
|
||||
height={13}
|
||||
alt={from_country_name.substring(0, 3)}
|
||||
/>
|
||||
<span className="pr-2 pl-1 text-sm text-gray-400">
|
||||
{from_country_name.substring(0, 3).toUpperCase()}
|
||||
</span>
|
||||
<span className="text-base font-semibold">
|
||||
{start_point} / {country_from}
|
||||
{from_city_name} / {from_country_name}
|
||||
</span>
|
||||
</div>
|
||||
|
||||
<div className="flex flex-col">
|
||||
<div className="flex items-center gap-4">
|
||||
<span className="text-base">{moving_type}</span>
|
||||
<span className="text-base">{formatted_transport}</span>
|
||||
<Image
|
||||
src={setMovingTypeIcon()}
|
||||
width={15}
|
||||
@@ -226,30 +245,37 @@ const SearchCard = ({
|
||||
/>
|
||||
</div>
|
||||
<div className="my-2 h-[2px] w-[165px] bg-gray-200" />
|
||||
<div className="text-sm">Дата доставки: {estimated_date.toLocaleDateString()}</div>
|
||||
<div className="text-sm">Дата доставки: {formatted_arrival}</div>
|
||||
</div>
|
||||
|
||||
<div className="-mb-[14px] flex flex-col">
|
||||
{user_request === 'Нужен перевозчик' ? (
|
||||
{userRequest === 'Нужен перевозчик' ? (
|
||||
<div className="text-sm text-gray-500">Доставить в:</div>
|
||||
) : (
|
||||
<div className="text-sm text-gray-500">Прибываю в:</div>
|
||||
)}
|
||||
|
||||
<div className="flex items-center">
|
||||
<Image src={country_to_icon} width={26} height={13} alt={country_to_code} />
|
||||
<span className="pr-2 pl-1 text-gray-400">{country_to_code}</span>
|
||||
<Image
|
||||
src={`/images/flags/${to_country_name.toLowerCase()}.png`}
|
||||
width={26}
|
||||
height={13}
|
||||
alt={to_country_name.substring(0, 3)}
|
||||
/>
|
||||
<span className="pr-2 pl-1 text-gray-400">
|
||||
{to_country_name.substring(0, 3).toUpperCase()}
|
||||
</span>
|
||||
<span className="text-base font-semibold">
|
||||
{end_point} / {country_to}
|
||||
{to_city_name} / {to_country_name}
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
{user_request === 'Могу перевезти' && (
|
||||
{userRequest === 'Могу перевезти' && (
|
||||
<div className="mt-3 ml-7 text-sm text-gray-500">
|
||||
<span className="text-sm font-normal">Прибытие:</span>{' '}
|
||||
<span className="text-sm font-semibold">{day_in?.toLocaleDateString()}</span>
|
||||
<span className="text-sm font-semibold">{formatted_arrival}</span>
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
|
||||
@@ -1,55 +1,7 @@
|
||||
import avatar from '../../public/images/avatar.png'
|
||||
import belarusIcon from '../../public/images/belarus.png'
|
||||
import russiaIcon from '../../public/images/russia.png'
|
||||
import { CargoType, TransportType } from '../types'
|
||||
|
||||
const userImg = avatar
|
||||
const blIcon = belarusIcon
|
||||
const ruIcon = russiaIcon
|
||||
|
||||
export const routes = 12845
|
||||
|
||||
export const data = [
|
||||
{
|
||||
id: 1123,
|
||||
username: 'John Doe',
|
||||
userImg: userImg,
|
||||
start_point: 'Минск',
|
||||
country_from: 'Беларусь',
|
||||
end_point: 'Москва',
|
||||
country_to: 'Россия',
|
||||
cargo_type: 'Документы',
|
||||
user_request: 'Нужен перевозчик',
|
||||
user_comment: 'Нужно перевезти документы из Минска в Москву',
|
||||
country_from_icon: blIcon,
|
||||
country_to_icon: ruIcon,
|
||||
country_from_code: 'BY',
|
||||
country_to_code: 'RU',
|
||||
moving_type: 'Авиатранспорт',
|
||||
estimated_date: new Date(2025, 4, 15),
|
||||
},
|
||||
{
|
||||
id: 2423,
|
||||
username: 'John Doe',
|
||||
userImg: userImg,
|
||||
start_point: 'Минск',
|
||||
country_from: 'Беларусь',
|
||||
end_point: 'Москва',
|
||||
country_to: 'Россия',
|
||||
cargo_type: 'Документы',
|
||||
user_request: 'Могу перевезти',
|
||||
user_comment: 'Нужно перевезти документы из Минска в Москву',
|
||||
moving_type: 'Автоперевозка',
|
||||
estimated_date: new Date(2025, 5, 18),
|
||||
country_from_icon: blIcon,
|
||||
country_to_icon: ruIcon,
|
||||
country_from_code: 'BY',
|
||||
country_to_code: 'RU',
|
||||
day_out: new Date(2025, 5, 21),
|
||||
day_in: new Date(2025, 5, 25),
|
||||
},
|
||||
]
|
||||
|
||||
export const cargo_types: CargoType[] = ['letter', 'package', 'passenger', 'parcel', 'cargo']
|
||||
|
||||
export const cargo_type_translations: Record<CargoType, string> = {
|
||||
|
||||
@@ -3,16 +3,17 @@ import Image from 'next/image'
|
||||
import AddressSelector from '@/components/AddressSelector'
|
||||
import SearchCard from '@/app/(urls)/search/components/SearchCard'
|
||||
import FAQ from '@/components/FAQ'
|
||||
import { data } from '@/app/constants'
|
||||
import { routes } from '@/app/constants'
|
||||
import Button from '@/components/ui/Button'
|
||||
import News from '@/components/News'
|
||||
import { getFAQs } from '@/lib/main/fetchFAQ'
|
||||
import { getNews } from '@/lib/main/fetchNews'
|
||||
import { getFirstRoutes } from '@/lib/main/fetchFirstRoutes'
|
||||
|
||||
export default async function Home() {
|
||||
const faqs = await getFAQs()
|
||||
const news = await getNews()
|
||||
const latestRoutes = await getFirstRoutes()
|
||||
|
||||
return (
|
||||
<div className="mx-auto flex max-w-[93%] flex-col items-center justify-center">
|
||||
@@ -69,9 +70,11 @@ export default async function Home() {
|
||||
{/* первые пять серч карточек -- бекенд??? */}
|
||||
<div className="mx-auto w-full max-w-[1250px]">
|
||||
<div className="grid w-full grid-cols-1 gap-4">
|
||||
{data.map(card => (
|
||||
<SearchCard key={card.id} {...card} />
|
||||
))}
|
||||
{Array.isArray(latestRoutes) && latestRoutes.length > 0 ? (
|
||||
latestRoutes.map(card => <SearchCard key={card.id} {...card} />)
|
||||
) : (
|
||||
<div className="py-4 text-center text-gray-600">Нет доступных маршрутов</div>
|
||||
)}
|
||||
</div>
|
||||
<div className="flex justify-center py-4">
|
||||
<Button
|
||||
|
||||
@@ -29,23 +29,24 @@ export interface ButtonProps {
|
||||
|
||||
export interface SearchCardProps {
|
||||
id: number
|
||||
username: string
|
||||
userImg: string | StaticImageData
|
||||
start_point: string
|
||||
country_from: string
|
||||
country_from_icon: string | StaticImageData
|
||||
country_from_code: string
|
||||
end_point: string
|
||||
country_to: string
|
||||
country_to_icon: string | StaticImageData
|
||||
country_to_code: string
|
||||
username: number
|
||||
userImg: string
|
||||
owner_type: string
|
||||
from_city_name: string
|
||||
from_country_name: string
|
||||
to_city_name: string
|
||||
to_country_name: string
|
||||
cargo_type: string
|
||||
user_request: string
|
||||
moving_type: string
|
||||
estimated_date: Date
|
||||
user_comment: string
|
||||
day_out?: Date
|
||||
day_in?: Date
|
||||
formatted_cargo_type: string
|
||||
formatted_transport: string
|
||||
type_transport: string
|
||||
comment: string
|
||||
departure_DT: string
|
||||
arrival_DT: string
|
||||
formatted_departure: string
|
||||
formatted_arrival: string
|
||||
country_from_icon: string
|
||||
country_to_icon: string
|
||||
}
|
||||
|
||||
export interface AccordionProps {
|
||||
|
||||
24
frontend/lib/main/fetchFirstRoutes.ts
Normal file
24
frontend/lib/main/fetchFirstRoutes.ts
Normal file
@@ -0,0 +1,24 @@
|
||||
import { SearchCardProps } from '@/app/types'
|
||||
|
||||
export async function getFirstRoutes(): Promise<SearchCardProps[]> {
|
||||
const API_URL = process.env.NEXT_PUBLIC_API_URL
|
||||
|
||||
try {
|
||||
const response = await fetch(`${API_URL}/latest-routes/`, {
|
||||
next: {
|
||||
revalidate: 86400, // один день
|
||||
},
|
||||
})
|
||||
|
||||
if (!response.ok) {
|
||||
console.error('Failed to fetch latest routes:', response.statusText)
|
||||
return []
|
||||
}
|
||||
|
||||
const routes = (await response.json()) as SearchCardProps[]
|
||||
return Array.isArray(routes) ? routes : []
|
||||
} catch (error) {
|
||||
console.error('Error fetching latest routes:', error)
|
||||
return []
|
||||
}
|
||||
}
|
||||
@@ -1,7 +1,6 @@
|
||||
import type { NextConfig } from 'next'
|
||||
|
||||
const API_URL =
|
||||
process.env.NEXT_PUBLIC_API_URL || 'http://127.0.0.1:8000/api/v1'
|
||||
const API_URL = process.env.NEXT_PUBLIC_API_URL || 'http://127.0.0.1:8000/api/v1'
|
||||
|
||||
const nextConfig: NextConfig = {
|
||||
images: {
|
||||
@@ -20,6 +19,10 @@ const nextConfig: NextConfig = {
|
||||
protocol: 'https',
|
||||
hostname: 'tripwb.com',
|
||||
},
|
||||
{
|
||||
protocol: 'https',
|
||||
hostname: 'i.ibb.co',
|
||||
},
|
||||
],
|
||||
},
|
||||
}
|
||||
|
||||
Binary file not shown.
|
Before Width: | Height: | Size: 11 KiB |
Binary file not shown.
|
Before Width: | Height: | Size: 8.2 KiB |
Reference in New Issue
Block a user