Files
tripwithbonus/frontend/app/(urls)/account/routes/page.tsx

284 lines
10 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'
import Link from 'next/link'
import useUserStore from '@/app/store/userStore'
export default function UserRoutes() {
const [routes, setRoutes] = useState<Route[]>([])
const [error, setError] = useState<string | null>(null)
const [loading, setLoading] = useState(true)
const { user } = useUserStore()
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: 'Не удалось поднять объявление',
})
}
}
const isLiteAccount = user?.account_type === 'lite'
const renderActionButtons = (route: Route) => {
if (isLiteAccount) {
return (
<div className="flex justify-between gap-2">
<Link
href="/payments"
className="flex items-center justify-center rounded-md border border-gray-300 bg-white/50 px-4 py-2 text-center text-sm font-medium text-gray-500 shadow-sm hover:bg-gray-50 focus:outline-none"
title="Доступно в тарифе Standart"
>
<span className="flex items-center gap-1">
<svg
xmlns="http://www.w3.org/2000/svg"
fill="none"
viewBox="0 0 24 24"
strokeWidth={1.5}
stroke="currentColor"
className="h-5 w-5"
>
<path
strokeLinecap="round"
strokeLinejoin="round"
d="M16.5 10.5V6.75a4.5 4.5 0 1 0-9 0v3.75m-.75 11.25h10.5a2.25 2.25 0 0 0 2.25-2.25v-6.75a2.25 2.25 0 0 0-2.25-2.25H6.75a2.25 2.25 0 0 0-2.25 2.25v6.75a2.25 2.25 0 0 0 2.25 2.25Z"
/>
</svg>
Поднять в выдаче
</span>
</Link>
<Link
href="/payments"
className="flex items-center justify-center rounded-md border border-gray-300 bg-white/50 px-4 py-2 text-center text-sm font-medium text-gray-500 shadow-sm hover:bg-gray-50 focus:outline-none"
title="Доступно в тарифе Standart"
>
<span className="flex items-center gap-1">
<svg
xmlns="http://www.w3.org/2000/svg"
fill="none"
viewBox="0 0 24 24"
strokeWidth={1.5}
stroke="currentColor"
className="h-5 w-5"
>
<path
strokeLinecap="round"
strokeLinejoin="round"
d="M16.5 10.5V6.75a4.5 4.5 0 1 0-9 0v3.75m-.75 11.25h10.5a2.25 2.25 0 0 0 2.25-2.25v-6.75a2.25 2.25 0 0 0-2.25-2.25H6.75a2.25 2.25 0 0 0-2.25 2.25v6.75a2.25 2.25 0 0 0 2.25 2.25Z"
/>
</svg>
Выделить
</span>
</Link>
</div>
)
}
return (
<div className="flex justify-between gap-2">
<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={route.is_highlighted ? 'Уже выделено!' : 'Выделить рамкой'}
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>
)
}
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 border-2'
}
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 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>
)}
{renderActionButtons(route)}
</div>
))}
</div>
)}
</div>
</div>
</div>
)
}