Files
tripwithbonus/frontend/app/(urls)/account/routes/page.tsx
2025-05-29 12:08:38 +03:00

219 lines
7.6 KiB
TypeScript
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
'use client'
import React, { useEffect, useState } from 'react'
import { Route } from '@/app/types'
import Button from '@/components/ui/Button'
import showToast from '@/components/ui/Toast'
import Loader from '@/components/ui/Loader'
export default function UserRoutes() {
const [routes, setRoutes] = useState<Route[]>([])
const [error, setError] = useState<string | null>(null)
const [loading, setLoading] = useState(true)
const fetchRoutes = async () => {
try {
const response = await fetch('/api/account/routes', {
method: 'GET',
headers: {
'Content-Type': 'application/json',
},
})
if (!response.ok) {
const error = await response.json()
throw new Error(error.message || 'Failed to fetch routes')
}
const data = await response.json()
setRoutes(data || [])
} catch (error) {
console.error('Error fetching routes:', error)
setError(error instanceof Error ? error.message : 'Не удалось загрузить маршруты')
} finally {
setLoading(false)
}
}
useEffect(() => {
fetchRoutes()
}, [])
const handleHighlight = async (route: Route) => {
try {
const response = await fetch(`/api/account/highlight`, {
method: 'PATCH',
headers: {
'Content-Type': 'application/json',
},
body: JSON.stringify({
route_id: route.id,
}),
})
if (!response.ok) {
const error = await response.json()
throw new Error(error.message || 'Failed to highlight route')
}
await fetchRoutes()
showToast({
type: 'success',
message: 'Ваше объявление выделено на сутки!',
})
} catch (error) {
console.error('Error highlighting route:', error)
showToast({
type: 'error',
message: 'Не удалось выделить объявление',
})
}
}
const handleUpper = async (route: Route) => {
try {
const response = await fetch(`/api/account/upper`, {
method: 'PATCH',
headers: {
'Content-Type': 'application/json',
},
body: JSON.stringify({
route_id: route.id,
}),
})
if (!response.ok) {
const error = await response.json()
throw new Error(error.message || 'Failed to upper route')
}
await fetchRoutes()
showToast({
type: 'success',
message: 'Ваше объявление поднято в выдаче!',
})
} catch (error) {
console.error('Error upper route:', error)
showToast({
type: 'error',
message: 'Не удалось поднять объявление',
})
}
}
if (loading) {
return <Loader />
}
if (error) {
return (
<div className="flex items-center justify-center py-12">
<div className="text-red-500">{error}</div>
</div>
)
}
const getBorderColor = (route: Route) => {
if (route.is_highlighted) {
return 'border-yellow-500'
}
return 'border'
}
return (
<div className="space-y-3">
<div className="overflow-hidden rounded-2xl shadow">
<div className="bg-white p-6 sm:p-8">
<div className="space-y-4">
<h1 className="text-2xl">Мои маршруты</h1>
</div>
{(!routes || routes.length === 0) && (
<div className="flex flex-col items-center justify-center py-12 text-gray-500">
<p className="text-lg">У вас пока нет созданных маршрутов</p>
<p className="text-sm">Создавайте заявки на перевозку, чтобы они отобразились тут</p>
</div>
)}
{routes.length > 0 && (
<div className="mt-4 space-y-4">
{routes.map(route => (
<div
key={route.id}
className={`space-y-4 rounded-2xl border bg-white p-6 transition-shadow hover:shadow-md ${getBorderColor(route)}`}
>
<div className="flex items-center justify-between">
<div className="text-sm text-gray-500">ID маршрута: #{route.id}</div>
<div
className={`rounded-full px-3 py-1 text-sm ${
route.owner_type === 'customer'
? 'bg-blue-100 text-blue-800'
: 'bg-green-100 text-green-800'
}`}
>
{route.owner_type === 'customer' ? 'Заказчик' : 'Перевозчик'}
</div>
</div>
<div className="flex flex-col space-y-1">
<div className="flex items-center space-x-3">
<div className="h-4 w-4 flex-shrink-0 rounded-full bg-blue-500" />
<div>
<div className="font-medium">
{route.from_city_name} / {route.from_country_name}
</div>
<div className="text-sm text-gray-600">{route.formatted_departure}</div>
</div>
</div>
<div className="ml-[7px] h-6 w-[2px] bg-gray-300" />
<div className="flex items-center space-x-3">
<div className="h-4 w-4 flex-shrink-0 rounded-full bg-green-500" />
<div>
<div className="font-medium">
{route.to_city_name} / {route.to_country_name}
</div>
<div className="text-sm text-gray-600">{route.formatted_arrival}</div>
</div>
</div>
</div>
<div className="flex flex-wrap gap-4 pt-2">
<div className="flex items-center space-x-2">
<div className="text-gray-500">Тип груза:</div>
<div className="font-medium">{route.formatted_cargo_type}</div>
</div>
<div className="flex items-center space-x-2">
<div className="text-gray-500">Способ перевозки:</div>
<div className="font-medium">{route.formatted_transport}</div>
</div>
</div>
{route.comment && (
<div className="border-t border-gray-300 pt-2">
<div className="text-sm text-gray-500">
<span className="text-gray-500">Комментарий:</span> {route.comment}
</div>
</div>
)}
<div className="flex justify-between">
<Button
text="Поднять объявление"
className="border border-gray-300 bg-white px-4 py-2 text-sm font-medium text-gray-700 shadow-sm hover:bg-gray-100 focus:outline-none"
onClick={() => handleUpper(route)}
/>
<Button
text="Выделить рамкой"
className="border border-gray-300 bg-white px-4 py-2 text-sm font-medium text-gray-700 shadow-sm hover:bg-gray-100 focus:outline-none"
onClick={() => handleHighlight(route)}
/>
</div>
</div>
))}
</div>
)}
</div>
</div>
</div>
)
}