From c897d25a7d33d35dda1aca884f8cddd0ca01d5f0 Mon Sep 17 00:00:00 2001 From: Timofey Date: Tue, 27 May 2025 14:02:50 +0300 Subject: [PATCH] search filter component --- docker-compose.yml | 127 ------------------ .../(urls)/search/[category]/[route]/page.tsx | 5 +- .../app/(urls)/search/[category]/page.tsx | 3 +- .../search/components/SearchFilters.tsx | 110 +++++++++++++++ frontend/components/ui/Selector.tsx | 12 +- 5 files changed, 126 insertions(+), 131 deletions(-) delete mode 100644 docker-compose.yml create mode 100644 frontend/app/(urls)/search/components/SearchFilters.tsx diff --git a/docker-compose.yml b/docker-compose.yml deleted file mode 100644 index 10cf6aa..0000000 --- a/docker-compose.yml +++ /dev/null @@ -1,127 +0,0 @@ -name: tripwb - -networks: - tripwb: - name: tripwb - driver: bridge - -services: - caddy: - image: caddy:alpine - container_name: tripwb-caddy-server - depends_on: - tripwb-backend-app: - condition: service_healthy - ports: - - 80:80 - - 443:443 - volumes: - - caddy-config:/config - - caddy-data:/data - - ./Caddyfile:/etc/caddy/Caddyfile:ro - networks: - - tripwb - - tripwb-frontend-app: - build: - context: frontend/ - # image: tripwb-frontend - container_name: tripwb-frontend-app - environment: - NEXT_PUBLIC_API_URL: ${NEXT_PUBLIC_API_URL} - NEXTAUTH_URL: ${NEXTAUTH_URL} - BACKEND_URL: ${BACKEND_URL} - env_file: - - .env - depends_on: - postgres: - condition: service_healthy - tripwb-backend-app: - condition: service_healthy - caddy: - condition: service_started - # ports: - # - 9000:3000 - networks: - - tripwb - tripwb-backend-app: - build: - context: backend/ - # image:tripwb-backend - container_name: tripwb-backend-app - environment: - SECRET_KEY: ${SECRET_KEY} - DEBUG_MODE: ${DEBUG_MODE} - API_KEY: ${API_KEY} - DB_USER: ${DB_USER} - DB_HOST: ${DB_HOST} - DB_NAME: ${DB_NAME} - DB_PASSWORD: ${DB_PASSWORD} - DB_PORT: ${DB_PORT} - healthcheck: - test: ['CMD', 'curl', '-s', '-o', '-f', 'http://tripwb-backend-app:8000'] - # interval: 5s - timeout: 5s - retries: 5 - env_file: - - .env - depends_on: - postgres: - condition: service_healthy - redis: - condition: service_healthy - volumes: - - tripwb-backend-app-uploads:/root/tripwb/uploads - # ports: - # - 8000:8000 - networks: - - tripwb - - # pgadmin-app: - # image: dpage/pgadmin4 - # container_name: pgadmin-app - # environment: - # DB_PORT: ${DB_PORT} - # DB_HOST: ${DB_HOST} - # DB_NAME: ${DB_NAME} - # DB_PASSWORD: ${DB_PASSWORD} - # DB_USER: ${DB_USER} - # BOT_TOKEN: ${BOT_TOKEN} - # CHAT_ID: ${BOT_TOKEN} - # JWT_SECRET: ${BOT_TOKEN} - # NEXT_PUBLIC_API_URL: ${NEXT_PUBLIC_API_URL} - # PGADMIN_DEFAULT_EMAIL: timofey.syr17@gmail.com - # PGADMIN_DEFAULT_PASSWORD: passWoRd - # depends_on: - # postgres: - # condition: service_healthy - # ports: - # - 81:80 - # networks: - # - tripwb - postgres: - image: postgres:alpine - restart: always - container_name: tripwb-db - env_file: backend/.env - environment: - POSTGRES_USER: ${DB_USER} - POSTGRES_DB: ${DB_NAME} - POSTGRES_PASSWORD: ${DB_PASSWORD} - healthcheck: - test: [CMD-SHELL, "sh -c 'pg_isready -U ${DB_USER} -d ${DB_NAME}'"] - interval: 10s - timeout: 5s - retries: 5 - # ports: - # - 5432:5432 - volumes: - - pg-data:/var/lib/postgresql/data - networks: - - tripwb - -volumes: - pg-data: - caddy-config: - caddy-data: - tripwb-backend-app-uploads: diff --git a/frontend/app/(urls)/search/[category]/[route]/page.tsx b/frontend/app/(urls)/search/[category]/[route]/page.tsx index e677e5f..ee14c5a 100644 --- a/frontend/app/(urls)/search/[category]/[route]/page.tsx +++ b/frontend/app/(urls)/search/[category]/[route]/page.tsx @@ -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 ( -
+

{results.length > 0 ? category === 'mover' @@ -75,6 +76,8 @@ export default async function SearchPage(props: RouteSearchPageProps) { : 'Результаты не найдены'}

+ + Загрузка результатов...
}>
{results.length > 0 ? ( diff --git a/frontend/app/(urls)/search/[category]/page.tsx b/frontend/app/(urls)/search/[category]/page.tsx index b124a8d..b69b434 100644 --- a/frontend/app/(urls)/search/[category]/page.tsx +++ b/frontend/app/(urls)/search/[category]/page.tsx @@ -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 { return { @@ -57,7 +58,7 @@ export default async function SearchPage(props: SearchPageProps) {

{params.category === 'mover' ? 'Поиск перевозчика' : 'Поиск посылки'}

- + Загрузка результатов...
}>
{results.length > 0 ? ( diff --git a/frontend/app/(urls)/search/components/SearchFilters.tsx b/frontend/app/(urls)/search/components/SearchFilters.tsx new file mode 100644 index 0000000..e8203e2 --- /dev/null +++ b/frontend/app/(urls)/search/components/SearchFilters.tsx @@ -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 = ({ onFiltersChange }) => { + const [selectedTransport, setSelectedTransport] = useState([]) + const [selectedPackageTypes, setSelectedPackageTypes] = useState([]) + + 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 ( +
+
+

Ищете что-то конкретное?

+ {hasActiveFilters && ( + + )} +
+
+
+ +
+
+ +
+
+
+ ) +} + +export default SearchFilters diff --git a/frontend/components/ui/Selector.tsx b/frontend/components/ui/Selector.tsx index 575c35e..66e8339 100644 --- a/frontend/components/ui/Selector.tsx +++ b/frontend/components/ui/Selector.tsx @@ -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(null) + + useEffect(() => { + setPortalTarget(document.body) + }, []) + return (
{label && ( @@ -103,7 +111,7 @@ const MultiSelect = ({ }, }), }} - menuPortalTarget={document.body} + menuPortalTarget={portalTarget} menuPosition="fixed" />