-
-
Notifications
You must be signed in to change notification settings - Fork 8
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Add sorting and searching functionality to bots page
- Loading branch information
Showing
12 changed files
with
392 additions
and
124 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,50 @@ | ||
'use client'; | ||
|
||
import cn from '@/lib/cn'; | ||
import { nanoid } from 'nanoid'; | ||
import { IoMdCheckmarkCircle } from 'react-icons/io'; | ||
import { Drawer } from 'vaul'; | ||
import { TbBoxMultiple, TbSquareRoundedChevronUp } from 'react-icons/tb'; | ||
import { HiSortAscending, HiSortDescending } from 'react-icons/hi'; | ||
import { TiStar } from 'react-icons/ti'; | ||
|
||
export default function SortingDrawer({ openState, setOpenState, state, setState }) { | ||
const sortings = { | ||
'Votes': <TbSquareRoundedChevronUp />, | ||
'Servers': <TbBoxMultiple />, | ||
'Most Reviewed': <TiStar />, | ||
'Newest': <HiSortAscending />, | ||
'Oldest': <HiSortDescending /> | ||
}; | ||
|
||
return ( | ||
<Drawer.Root shouldScaleBackground={true} closeThreshold={0.5} open={openState} onOpenChange={setOpenState}> | ||
<Drawer.Portal> | ||
<Drawer.Content className='outline-none gap-y-1 p-4 z-[10001] bg-secondary flex flex-col rounded-t-3xl h-[85%] fixed bottom-0 left-0 right-0'> | ||
<div className='mx-auto w-12 h-1.5 flex-shrink-0 rounded-full bg-quaternary mb-8' /> | ||
|
||
{Object.keys(sortings).map(sort => ( | ||
<button | ||
key={nanoid()} | ||
onClick={() => { | ||
setState(sort); | ||
setOpenState(false); | ||
}} | ||
className={cn( | ||
'flex items-center justify-between px-4 py-3 text-base font-medium rounded-lg disabled:pointer-events-none', | ||
state === sort ? 'pointer-events-none bg-quaternary text-primary' : 'hover:bg-quaternary text-tertiary hover:text-primary' | ||
)} | ||
> | ||
<span className='flex items-center gap-x-2'> | ||
{sortings[sort]} | ||
{sort} | ||
</span> | ||
{state === sort && <IoMdCheckmarkCircle />} | ||
</button> | ||
))} | ||
</Drawer.Content> | ||
<Drawer.Overlay className='fixed inset-0 bg-white/40 dark:bg-black/40 z-[10000]' /> | ||
</Drawer.Portal> | ||
</Drawer.Root> | ||
); | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
96 changes: 96 additions & 0 deletions
96
client/app/(bots)/bots/components/Hero/SearchInput/index.jsx
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,96 @@ | ||
import { FiSearch, FiX } from 'react-icons/fi'; | ||
import { motion } from 'framer-motion'; | ||
import useSearchStore from '@/stores/bots/search'; | ||
import { useEffect, useState } from 'react'; | ||
import { toast } from 'sonner'; | ||
import { useShallow } from 'zustand/react/shallow'; | ||
|
||
export default function SearchInput() { | ||
const [value, setValue] = useState(''); | ||
const { loading, search, fetchBots } = useSearchStore(useShallow(state => ({ | ||
loading: state.loading, | ||
search: state.search, | ||
fetchBots: state.fetchBots | ||
}))); | ||
|
||
function validateValue(throwError = true) { | ||
function returnError(message) { | ||
if (throwError) toast.error(message); | ||
return false; | ||
} | ||
|
||
if (!value) return returnError('Please enter a valid search query to find bots.'); | ||
|
||
const trimmedValue = value.trim(); | ||
if (trimmedValue.length <= 0) return returnError('Please enter a valid search query to find bots.'); | ||
if (trimmedValue.length < 3) return returnError('The search query is too short. Please enter a minimum of 3 characters.'); | ||
if (trimmedValue.length > 100) return returnError('The search query is too long. Please enter a maximum of 100 characters.'); | ||
|
||
return trimmedValue; | ||
} | ||
|
||
const sequenceTransition = { | ||
duration: 0.25, | ||
type: 'spring', | ||
stiffness: 260, | ||
damping: 20 | ||
}; | ||
|
||
useEffect(() => { | ||
setValue(search); | ||
}, [search]); | ||
|
||
return ( | ||
<div className='flex mt-8 gap-x-2'> | ||
<div className='relative flex items-center w-full'> | ||
<motion.input | ||
type="text" | ||
placeholder="Search for a bot by id, description, or category..." | ||
value={value} | ||
disabled={loading} | ||
className='w-full p-3 font-medium rounded-md outline-none disabled:pointer-events-none disabled:opacity-70 text-secondary placeholder-placeholder bg-secondary hover:bg-tertiary focus-visible:bg-tertiary' | ||
onChange={event => setValue(event.target.value)} | ||
autoComplete='off' | ||
maxLength={100} | ||
spellCheck='false' | ||
onKeyUp={event => { | ||
if (event.key === 'Enter') { | ||
const validatedValue = validateValue(); | ||
if (validatedValue) fetchBots(validatedValue); | ||
} | ||
}} | ||
initial={{ opacity: 0, y: -25 }} | ||
animate={{ opacity: 1, y: 0 }} | ||
transition={{ ...sequenceTransition, delay: 0.3 }} | ||
/> | ||
|
||
{search && ( | ||
<motion.button | ||
className='absolute right-2 p-1.5 rounded-md text-secondary hover:bg-tertiary' | ||
onClick={() => { | ||
setValue(''); | ||
fetchBots(''); | ||
}} | ||
disabled={loading} | ||
> | ||
<FiX className='text-xl' /> | ||
</motion.button> | ||
)} | ||
</div> | ||
|
||
<motion.button | ||
className='p-3 rounded-md bg-secondary text-secondary hover:bg-tertiary disabled:pointer-events-none disabled:opacity-70' | ||
onClick={() => { | ||
const validatedValue = validateValue(); | ||
if (validatedValue) fetchBots(validatedValue); | ||
}} | ||
disabled={loading || validateValue(false) === false} | ||
initial={{ opacity: 0, y: -25 }} | ||
animate={{ opacity: 1, y: 0 }} | ||
transition={{ ...sequenceTransition, delay: 0.3 }} | ||
> | ||
<FiSearch className='text-xl' /> | ||
</motion.button> | ||
</div> | ||
); | ||
} |
87 changes: 0 additions & 87 deletions
87
client/app/(bots)/bots/components/Hero/TopCategories/index.jsx
This file was deleted.
Oops, something went wrong.
Oops, something went wrong.