search filter component
This commit is contained in:
@@ -2,6 +2,7 @@ import React, { Suspense } from 'react'
|
||||
import type { Metadata } from 'next'
|
||||
import SearchCard from '../../components/SearchCard'
|
||||
import { SearchCardProps, RouteSearchPageProps } from '@/app/types'
|
||||
import SearchFilters from '../../components/SearchFilters'
|
||||
|
||||
async function fetchSearch(category: string, from: string, to: string) {
|
||||
const response = await fetch(
|
||||
@@ -66,7 +67,7 @@ export default async function SearchPage(props: RouteSearchPageProps) {
|
||||
const { results, count } = await fetchSearch(category, fromCity, toCity)
|
||||
|
||||
return (
|
||||
<div className="container mx-auto p-4">
|
||||
<div className="container mx-auto space-y-10 p-4">
|
||||
<h1 className="mb-4 text-2xl font-bold">
|
||||
{results.length > 0
|
||||
? category === 'mover'
|
||||
@@ -75,6 +76,8 @@ export default async function SearchPage(props: RouteSearchPageProps) {
|
||||
: 'Результаты не найдены'}
|
||||
</h1>
|
||||
|
||||
<SearchFilters />
|
||||
|
||||
<Suspense fallback={<div>Загрузка результатов...</div>}>
|
||||
<div className="space-y-4">
|
||||
{results.length > 0 ? (
|
||||
|
||||
@@ -3,6 +3,7 @@ import type { Metadata } from 'next'
|
||||
import SearchCard from '../components/SearchCard'
|
||||
import { SearchCardProps, SearchPageProps } from '@/app/types'
|
||||
import { fetchRoutes } from '@/lib/search/fetchRoutes'
|
||||
import SearchFilters from '../components/SearchFilters'
|
||||
|
||||
export async function generateMetadata(): Promise<Metadata> {
|
||||
return {
|
||||
@@ -57,7 +58,7 @@ export default async function SearchPage(props: SearchPageProps) {
|
||||
<h1 className="mb-4 text-2xl font-bold">
|
||||
{params.category === 'mover' ? 'Поиск перевозчика' : 'Поиск посылки'}
|
||||
</h1>
|
||||
|
||||
<SearchFilters />
|
||||
<Suspense fallback={<div>Загрузка результатов...</div>}>
|
||||
<div className="space-y-4">
|
||||
{results.length > 0 ? (
|
||||
|
||||
110
frontend/app/(urls)/search/components/SearchFilters.tsx
Normal file
110
frontend/app/(urls)/search/components/SearchFilters.tsx
Normal file
@@ -0,0 +1,110 @@
|
||||
'use client'
|
||||
|
||||
import React, { useState } from 'react'
|
||||
import MultiSelect from '@/components/ui/Selector'
|
||||
|
||||
const transportOptions = [
|
||||
{ id: 1, value: 'road', label: 'Авто' },
|
||||
{ id: 2, value: 'avia', label: 'Авиа' },
|
||||
{ id: 3, value: 'both', label: 'Любой' },
|
||||
]
|
||||
|
||||
const packageTypeOptions = [
|
||||
{ id: 1, value: 'letter', label: 'Письмо или Документы' },
|
||||
{ id: 2, value: 'package', label: 'Посылка (до 30кг)' },
|
||||
{ id: 3, value: 'passenger', label: 'Попутчик' },
|
||||
{ id: 4, value: 'parcel', label: 'Бандероль (до 5кг)' },
|
||||
{ id: 5, value: 'cargo', label: 'Груз (свыше 30 кг)' },
|
||||
]
|
||||
|
||||
interface SearchFiltersProps {
|
||||
onFiltersChange?: (filters: { transport: number[]; packageTypes: number[] }) => void
|
||||
}
|
||||
|
||||
const SearchFilters: React.FC<SearchFiltersProps> = ({ onFiltersChange }) => {
|
||||
const [selectedTransport, setSelectedTransport] = useState<number[]>([])
|
||||
const [selectedPackageTypes, setSelectedPackageTypes] = useState<number[]>([])
|
||||
|
||||
const handleTransportChange = (e: { target: { value: number[] } }) => {
|
||||
setSelectedTransport(e.target.value)
|
||||
onFiltersChange?.({
|
||||
transport: e.target.value,
|
||||
packageTypes: selectedPackageTypes,
|
||||
})
|
||||
}
|
||||
|
||||
const handlePackageTypesChange = (e: { target: { value: number[] } }) => {
|
||||
setSelectedPackageTypes(e.target.value)
|
||||
onFiltersChange?.({
|
||||
transport: selectedTransport,
|
||||
packageTypes: e.target.value,
|
||||
})
|
||||
}
|
||||
|
||||
const handleReset = () => {
|
||||
setSelectedTransport([])
|
||||
setSelectedPackageTypes([])
|
||||
onFiltersChange?.({
|
||||
transport: [],
|
||||
packageTypes: [],
|
||||
})
|
||||
}
|
||||
|
||||
const hasActiveFilters = selectedTransport.length > 0 || selectedPackageTypes.length > 0
|
||||
|
||||
return (
|
||||
<div className="w-full rounded-2xl bg-white p-6 shadow-sm">
|
||||
<div className="mb-6 flex items-center justify-between">
|
||||
<h2 className="text-lg font-semibold text-gray-900">Ищете что-то конкретное?</h2>
|
||||
{hasActiveFilters && (
|
||||
<button
|
||||
onClick={handleReset}
|
||||
className="hover:text-orange/80 flex items-center text-sm font-medium text-blue-600"
|
||||
>
|
||||
<svg
|
||||
className="mr-2 h-4 w-4"
|
||||
fill="none"
|
||||
stroke="currentColor"
|
||||
viewBox="0 0 24 24"
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
>
|
||||
<path
|
||||
strokeLinecap="round"
|
||||
strokeLinejoin="round"
|
||||
strokeWidth={2}
|
||||
d="M6 18L18 6M6 6l12 12"
|
||||
/>
|
||||
</svg>
|
||||
Сбросить фильтры
|
||||
</button>
|
||||
)}
|
||||
</div>
|
||||
<div className="grid gap-6 md:grid-cols-2">
|
||||
<div className="space-y-2">
|
||||
<MultiSelect
|
||||
value={selectedTransport}
|
||||
handleChange={handleTransportChange}
|
||||
label="Способ перевозки"
|
||||
placeholder="Выберите способ перевозки"
|
||||
name="transport"
|
||||
options={transportOptions}
|
||||
className="w-full"
|
||||
/>
|
||||
</div>
|
||||
<div className="space-y-2">
|
||||
<MultiSelect
|
||||
value={selectedPackageTypes}
|
||||
handleChange={handlePackageTypesChange}
|
||||
label="Тип посылки"
|
||||
placeholder="Выберите тип посылки"
|
||||
name="packageType"
|
||||
options={packageTypeOptions}
|
||||
className="w-full"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
export default SearchFilters
|
||||
@@ -1,4 +1,6 @@
|
||||
import React from 'react'
|
||||
'use client'
|
||||
|
||||
import React, { useState, useEffect } from 'react'
|
||||
import Select from 'react-select'
|
||||
import { MultiSelectProps } from '@/app/types'
|
||||
|
||||
@@ -12,6 +14,12 @@ const MultiSelect = ({
|
||||
className = '',
|
||||
noOptionsMessage = 'Нет доступных опций',
|
||||
}: MultiSelectProps) => {
|
||||
const [portalTarget, setPortalTarget] = useState<HTMLElement | null>(null)
|
||||
|
||||
useEffect(() => {
|
||||
setPortalTarget(document.body)
|
||||
}, [])
|
||||
|
||||
return (
|
||||
<div className={className}>
|
||||
{label && (
|
||||
@@ -103,7 +111,7 @@ const MultiSelect = ({
|
||||
},
|
||||
}),
|
||||
}}
|
||||
menuPortalTarget={document.body}
|
||||
menuPortalTarget={portalTarget}
|
||||
menuPosition="fixed"
|
||||
/>
|
||||
</div>
|
||||
|
||||
Reference in New Issue
Block a user