Skip to content

Commit

Permalink
feat: autocomplete tags reusable component
Browse files Browse the repository at this point in the history
  • Loading branch information
warmachine028 committed Nov 30, 2024
1 parent 38ca69d commit 41336c1
Show file tree
Hide file tree
Showing 6 changed files with 116 additions and 64 deletions.
26 changes: 21 additions & 5 deletions client/src/api/tags.js
Original file line number Diff line number Diff line change
@@ -1,11 +1,27 @@
import { api, handleApiError } from '@/api'
import { movies } from '@/data'
import { sleep } from '@/lib/utils'

export const getTrendingTags = async () => {
try {
const { data } = await api.get('/tags/trending')
return data
} catch (error) {
try {
const { data } = await api.get('/tags/trending')
return data
} catch (error) {
throw handleApiError(error)
}
}
}

export const searchTags = async (searchTerm) => {
try {
// Filter movies based on search term
return movies
.filter((movie) =>
movie.title
.toLowerCase()
.includes(searchTerm?.toLowerCase() || '')
)
.map((movie) => movie.title)
} catch (error) {
throw handleApiError(error)
}
}
76 changes: 76 additions & 0 deletions client/src/components/TagsAutocompleteInput.jsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,76 @@
import { useSearchTags } from '@/hooks'
import { AutoAwesome } from '@mui/icons-material'
import {
Autocomplete,
CircularProgress,
IconButton,
InputAdornment,
TextField
} from '@mui/material'
import { useState } from 'react'

const TagsAutocompleteInput = ({ formData, setFormData, error }) => {
const [input, setInput] = useState('')

const { data: options = [], isLoading } = useSearchTags(input)

const generateTags = () => {
const aIGenerateTags = ['social', 'media', 'post']
setFormData({
...formData,
tags: [
...new Set([...formData.tags, ...aIGenerateTags].slice(0, 8))
]
})
}
const handleChange = (_, value) =>
setFormData((prevData) => ({
...prevData,
tags: value.length > 8 ? value.slice(-8) : value
}))

return (
<Autocomplete
multiple
freeSolo
options={options}
loading={isLoading}
inputValue={input}
value={formData.tags}
onChange={handleChange}
onInputChange={(_, newInput) => setInput(newInput)}
disableClearable
renderInput={(params) => (
<TextField
{...params}
label="Tags"
error={error}
slotProps={{
input: {
...params.InputProps,
type: 'search',
endAdornment: (
<InputAdornment position="end">
{isLoading ? (
<CircularProgress size={20} />
) : (
<IconButton
onClick={generateTags}
disabled={formData.tags.length >= 8}
edge="end"
size="small"
>
<AutoAwesome />
</IconButton>
)}
</InputAdornment>
)
}
}}
/>
)}
/>
)
}

export default TagsAutocompleteInput
65 changes: 6 additions & 59 deletions client/src/components/forms/CreatePost.jsx
Original file line number Diff line number Diff line change
@@ -1,5 +1,4 @@
import {
Autocomplete,
Box,
Button,
ButtonGroup,
Expand All @@ -9,16 +8,13 @@ import {
FormHelperText,
IconButton,
Input,
InputAdornment,
Stack,
TextField,
Tooltip,
Typography
} from '@mui/material'
import { movies } from '@/data'
import {
AddAPhotoOutlined,
AutoAwesome,
Close,
Visibility,
VisibilityOff
Expand All @@ -28,6 +24,7 @@ import { useAuth } from '@clerk/clerk-react'
import { Link } from 'react-router'
import { useCreatePost } from '@/hooks'
import { convertToBase64 } from '@/lib/utils'
import { TagsAutocompleteInput } from '@/components'

const Form = () => {
const { isLoaded } = useAuth()
Expand Down Expand Up @@ -96,15 +93,7 @@ const Form = () => {
setErrors(newErrors)
return valid
}
const generateTags = () => {
const aIGenerateTags = ['social', 'media', 'post']
setFormData({
...formData,
tags: [
...new Set([...formData.tags, ...aIGenerateTags].slice(0, 8))
]
})
}

const handleClear = () => {
setFormData(initialData)
setPreview(null)
Expand All @@ -127,35 +116,6 @@ const Form = () => {
}
}

const handleSearchInput = (params) => {
return (
<TextField
{...params}
label="Tags"
error={Boolean(errors.tags)}
name="tags"
slotProps={{
input: {
...params.InputProps,
type: 'search',
endAdornment: (
<InputAdornment position="end">
<IconButton
onClick={generateTags}
disabled={formData.tags.length >= 8}
edge="end"
size="small"
>
<AutoAwesome />
</IconButton>
</InputAdornment>
)
}
}}
/>
)
}

return (
<Stack component="form" onSubmit={handleSubmit} spacing={1} p={2}>
<Typography variant="h6" gutterBottom textAlign="center">
Expand Down Expand Up @@ -260,23 +220,10 @@ const Form = () => {
</FormHelperText>
</FormControl>
<FormControl fullWidth error={Boolean(errors.tags)}>
<Autocomplete
multiple
freeSolo
options={movies.map((option) => option.title)}
renderInput={handleSearchInput}
value={formData.tags}
onChange={(_, value) => {
setFormData((prevData) => ({
...prevData,
tags: value.length > 8 ? value.slice(-8) : value
}))
setErrors({ ...errors, tags: '' })
}}
onInputChange={(_, value) =>
formData.tags.length < 8 ? value : ''
}
disableClearable
<TagsAutocompleteInput
formData={formData}
setFormData={setFormData}
error={Boolean(errors.tags)}
/>
<FormHelperText sx={{ m: 0 }}>{errors.tags}</FormHelperText>
</FormControl>
Expand Down
1 change: 1 addition & 0 deletions client/src/components/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@ export { default as Image } from './Image'
export { default as ReactButton } from './ReactButton'
export { default as LikeButton } from './LikeButton'
export { default as ShareButton } from './ShareButton'
export { default as TagsAutocompleteInput } from './TagsAutocompleteInput'
export * from './skeletons'
export * from './forms'
export * from './dialogues'
1 change: 1 addition & 0 deletions client/src/hooks/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -15,3 +15,4 @@ export * from './posts'
export * from './users'
export * from './reactions'
export * from './comments'
export * from './tags'
11 changes: 11 additions & 0 deletions client/src/hooks/tags.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
import { useQuery } from '@tanstack/react-query'
import { searchTags } from '@/api'

export const useSearchTags = (input) =>
useQuery({
queryKey: ['tags', input],
queryFn: () => searchTags(input),
enabled: !!input,
staleTime: 1000 * 60 * 5,
gcTime: 1000 * 60 * 30
})

0 comments on commit 41336c1

Please sign in to comment.