Skip to content

Commit

Permalink
Merge pull request #26 from JasonCrk/issue-22-search-events-page
Browse files Browse the repository at this point in the history
Search events page
  • Loading branch information
JasonCrk authored Nov 22, 2023
2 parents 64418ed + 353f00c commit c41d56f
Show file tree
Hide file tree
Showing 9 changed files with 224 additions and 20 deletions.
3 changes: 2 additions & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,8 @@
},
"dependencies": {
"@hookform/resolvers": "^3.3.2",
"@tanstack/react-query": "^5.0.5",
"@tanstack/react-query": "^5.8.4",
"@tanstack/react-query-devtools": "^5.8.4",
"axios": "^1.6.0",
"bootstrap": "5.3.2",
"firebase": "^10.4.0",
Expand Down
34 changes: 27 additions & 7 deletions pnpm-lock.yaml

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

46 changes: 34 additions & 12 deletions src/components/Navbar.component.tsx
Original file line number Diff line number Diff line change
@@ -1,33 +1,55 @@
import { FC } from 'react'
import { FC, KeyboardEvent, useState } from 'react'

import { Link } from 'react-router-dom'
import { Link, useNavigate, useSearchParams } from 'react-router-dom'

import NavLink from './NavLink.component'
import UserOptions from './UserOptions.component'

import { FiSearch } from 'react-icons/fi'

const Navbar: FC = () => {
const [searchEventsParams] = useSearchParams()
const [searchQuery, setSearchQuery] = useState<string>(
searchEventsParams.get('q') ?? ''
)

const navigate = useNavigate()

const handleSearchPublicEvents = (event: KeyboardEvent<HTMLInputElement>) => {
const searchQuery = event.currentTarget.value
if (event.key === 'Enter' && searchQuery !== '') {
const searchParams = new URLSearchParams()
searchParams.append('q', searchQuery)

navigate('/search?' + searchParams)
}
}

return (
<nav className='navbar navbar-expand-lg navbar-dark bg-black'>
<div className='container-md'>
<Link className='navbar-brand fw-bold' to='/'>
Master Planner Event
</Link>

{/* <div className='collapse navbar-collapse'>
<form>
<Input
register={}
placeholder='Search...'
adornment={() => <FiSearch />}
style={{ width: '300px' }}
<div className='collapse navbar-collapse'>
<div className='input-group w-75'>
<span className='input-group-text'>
<FiSearch />
</span>
<input
placeholder='Buscar...'
value={searchQuery}
onChange={e => setSearchQuery(e.currentTarget.value)}
className='form-control'
onKeyDown={handleSearchPublicEvents}
/>
</form>
</div> */}
</div>
</div>

<div className='d-flex align-items-center gap-3'>
<ul className='nav nav-underline d-flex'>
<NavLink href='/'>Home</NavLink>
<NavLink href='/public-events'>Public events</NavLink>
<NavLink href='/networks'>Networks</NavLink>
<NavLink href='/notifications'>Notifications</NavLink>
</ul>
Expand Down
46 changes: 46 additions & 0 deletions src/components/SelectCategory.component.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,46 @@
import { ChangeEvent, FC } from 'react'

import { useQuery } from '@tanstack/react-query'

import { UseFormRegister } from 'react-hook-form'

import { getAllCategories } from '../services/category.service'

import Select from './Select.component'

interface Props {
register: UseFormRegister<any>
onChange: (event: ChangeEvent<HTMLSelectElement>) => void
}

const SelectCategory: FC<Props> = ({ register, onChange }) => {
const { isLoading, data: categories } = useQuery({
queryKey: ['categories'],
queryFn: () => getAllCategories(),
refetchOnWindowFocus: false,
refetchOnMount: false,
})

return (
<Select
id='selectCategory'
defaultOption='No selected'
name='category'
register={register}
isLoading={isLoading}
label='Category'
onChange={onChange}
options={() => (
<>
{categories?.data.map(category => (
<option key={category.id} value={category.name}>
{category.name}
</option>
))}
</>
)}
/>
)
}

export default SelectCategory
2 changes: 2 additions & 0 deletions src/main.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ import { RouterProvider } from 'react-router-dom'
import router from './router'

import { QueryClientProvider, QueryClient } from '@tanstack/react-query'
import { ReactQueryDevtools } from '@tanstack/react-query-devtools'

import { AlertContextProvider } from './context/alert/AlertContextProvider'

Expand All @@ -20,6 +21,7 @@ ReactDOM.createRoot(document.getElementById('root')!).render(
<AlertContextProvider>
<RouterProvider router={router} />
</AlertContextProvider>
<ReactQueryDevtools initialIsOpen={false} />
</QueryClientProvider>
</React.StrictMode>
)
5 changes: 5 additions & 0 deletions src/models/event.model.ts
Original file line number Diff line number Diff line change
Expand Up @@ -108,3 +108,8 @@ export interface SearchEventsParticipatingParams {
sortBy?: string
category?: CategoryName
}

export interface SearchEventsParams {
searchQuery: string
category?: CategoryName
}
90 changes: 90 additions & 0 deletions src/pages/SearchEvents.page.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,90 @@
import { Link, useSearchParams } from 'react-router-dom'

import { useQuery } from '@tanstack/react-query'

import { useForm } from 'react-hook-form'

import { SearchEventsParams } from '../models/event.model'

import { searchEvents } from '../services/event.service'

import EventCardSkeleton from '../components/skeleton/EventCardSkeleton.component'
import EventCard from '../components/EventCard.component'
import SelectCategory from '../components/SelectCategory.component'

function SearchEvents() {
const [searchParams, setSearchParams] = useSearchParams()

const { handleSubmit, register, setValue } = useForm<SearchEventsParams>({
defaultValues: {
searchQuery: searchParams.get('q') ?? '',
},
})

const {
data: events,
isLoading: isLoadingEvents,
isRefetching,
} = useQuery({
queryKey: [
'search',
{ q: searchParams.get('q'), category: searchParams.get('category') },
],
queryFn: () => searchEvents(searchParams),
enabled: searchParams.get('q') !== null || searchParams.get('q') !== '',
refetchOnWindowFocus: false,
})

const handleSearchEvents = handleSubmit(data => {
const searchQuery = searchParams.get('q')
if (searchQuery !== null) {
if (data.category && data.category !== null) {
setSearchParams({ q: searchQuery, category: data.category })
return
}

setSearchParams({ q: searchQuery })
}
})

return (
<main className='d-flex mt-4 gap-3'>
<section className='d-flex flex-column gap-3' style={{ width: '65%' }}>
{isRefetching ? (
<div>Nada</div>
) : isLoadingEvents || !events ? (
[...Array(3)].map(() => (
<EventCardSkeleton key={crypto.randomUUID()} width='100%' />
))
) : events.data.length > 0 ? (
events.data.map(event => (
<EventCard key={event.id} {...event} width='100%' />
))
) : (
<h5 className='text-center'>No events found</h5>
)}
</section>

<aside style={{ width: '35%' }}>
<div className='card mb-2'>
<form className='card-body'>
<h5 className='mb-1'>Filters</h5>
<SelectCategory
register={register}
onChange={event => {
setValue('category', event.currentTarget.value)
handleSearchEvents()
}}
/>
</form>
</div>

<Link to='/create' className='btn btn-primary w-100'>
New Event
</Link>
</aside>
</main>
)
}

export default SearchEvents
5 changes: 5 additions & 0 deletions src/router.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ import Login from './pages/Login.page'
import Register from './pages/Register.page'

import Home from './pages/Home.page'
import SearchEvents from './pages/SearchEvents.page'
import CreateEvent from './pages/CreateEvent.page'
import EventDetails from './pages/EventDetails.page'
import UserProfile from './pages/UserProfile.page'
Expand Down Expand Up @@ -40,6 +41,10 @@ const router = createBrowserRouter([
index: true,
element: <Home />,
},
{
path: '/search',
element: <SearchEvents />,
},
{
path: '/create',
element: <CreateEvent />,
Expand Down
13 changes: 13 additions & 0 deletions src/services/event.service.ts
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,19 @@ export const searchEventsParticipating = async (
.then(response => response.data)
}

export const searchEvents = async (
params: URLSearchParams
): Promise<ListResponse<EventItem>> => {
const accessToken = useAuthStore.getState().accessToken
return eventBaseEndpoint
.get<ListResponse<EventItem>>('/search?' + params.toString(), {
headers: {
Authorization: `Bearer ${accessToken}`,
},
})
.then(response => response.data)
}

export const getEventById = async (eventId: EventId): Promise<EventDetails> => {
const accessToken = useAuthStore.getState().accessToken
return eventBaseEndpoint
Expand Down

0 comments on commit c41d56f

Please sign in to comment.