Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

I86 search by pet names in contacts page #88

Merged
merged 20 commits into from
Jul 21, 2022
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
754 changes: 9 additions & 745 deletions mockData/CONTACT_DATA.json

Large diffs are not rendered by default.

11 changes: 1 addition & 10 deletions mockData/PROFILE_DATA.json
Original file line number Diff line number Diff line change
Expand Up @@ -3,16 +3,7 @@
"firstName": "John",
"lastName": "Doe",
"desc": "description",
"pets": [
{ "id": "62c6f9a7a9ff10abfef8f4b0", "name": "Zoe", "notes": "Is Moth" },
{ "id": "62c6f9a73d8c64def8c87ca7", "name": "Kato", "notes": "Guinea Pig" },
{ "id": "62c6f9a70b2d3c4868120418", "name": "John", "notes": "Capubara" },
{
"id": "62c6f9a7762bb5632137013a",
"name": "Goggy",
"notes": "Praying Mantis"
}
],
"pets": "Pongo, Casper",
"email": "tiamcmahon@kegular.com",
"phone": "(886) 578-3300",
"streetAddress": "691 Cleveland Street",
Expand Down
89 changes: 9 additions & 80 deletions src/components/Contact/contactform.tsx
Original file line number Diff line number Diff line change
@@ -1,13 +1,11 @@
import React, { useEffect } from 'react'
import { Dispatch, SetStateAction, useState } from 'react'
import { PlusIcon, XIcon } from '@heroicons/react/outline'
import tw from 'tailwind-styled-components'
import { v4 as uuidv4 } from 'uuid'

import Avatar from '@/components/Contact/avatar'
import RegionSelector from '@/components/Contact/regiondropdown'
import TagSelector from '@/components/Contact/tagdropdown'
import type { Contact, Pet } from '@/types/types'
import type { Contact } from '@/types/types'

type ContactInfoProps = {
contact: Contact
Expand All @@ -16,24 +14,10 @@ type ContactInfoProps = {
}

const ContactForm = ({ contact, image, setIsEditing }: ContactInfoProps) => {
const [pets, setPets] = useState<Pet[]>(contact.pets)
const [regions, setRegions] = useState(contact.region)
const [tags, setTags] = useState(contact.tags)
const [contactForm, setContactForm] = useState(contact)

function addPet() {
const newPetField = {
id: uuidv4(),
name: '',
notes: ''
}
setPets((pets) => [...pets, newPetField])
}

const removePet = (pet: Pet) => {
setPets([...pets.filter((p) => p.name !== pet.name)])
}

const handleInputChange = (
e:
| React.ChangeEvent<HTMLInputElement>
Expand All @@ -43,31 +27,13 @@ const ContactForm = ({ contact, image, setIsEditing }: ContactInfoProps) => {
setContactForm({ ...contactForm, [name]: value })
}

const handlePetChange = (
petIndex: number,
e:
| React.ChangeEvent<HTMLInputElement>
| React.ChangeEvent<HTMLTextAreaElement>
) => {
const { name, value } = e.target
const updatedPet = pets[petIndex]
updatedPet[name as keyof Pet] = value
// Only set the updated pet but keep the other existing pets
setPets([
...pets.slice(0, petIndex),
updatedPet,
...pets.slice(petIndex + 1, pets.length)
])
}

useEffect(() => {
setContactForm((contactForm) => ({
...contactForm,
tags: tags,
region: regions,
pets: pets
region: regions
}))
}, [regions, pets, tags])
}, [regions, tags])

// TODO: Submit ContactForm to database
const submitForm = (e: React.FormEvent) => {
Expand Down Expand Up @@ -182,42 +148,12 @@ const ContactForm = ({ contact, image, setIsEditing }: ContactInfoProps) => {
<label htmlFor='pets' className='text-dark-red'>
Pets
</label>
{pets.map((pet, i) => (
<div key={pet.id}>
<PetContainer className='py-1'>
<div className='flex w-full justify-between'>
<label htmlFor={pet.name} className='text-dark-red'>
Name
</label>
<button type='button' onClick={() => removePet(pet)}>
<XIcon className='h-5 w-5 text-dark-red' />
</button>
</div>
<input
name='name'
defaultValue={pet.name}
className='mb-2 w-full rounded-lg border border-gray-300 pl-1'
onChange={(e) => handlePetChange(i, e)}
/>

<label htmlFor={pet.notes} className='text-dark-red'>
Notes
</label>
<textarea
name='notes'
defaultValue={pet.notes}
className='mb-2 w-full rounded-lg border border-gray-300 pl-1'
onChange={(e) => handlePetChange(i, e)}
/>
</PetContainer>
</div>
))}
{/* Plus icon that adds a new pet container */}
<div className='flex justify-center'>
<button type='button' onClick={addPet}>
<PlusIcon className='h-7 w-7 rounded-full bg-white p-1 text-dark-red' />
</button>
</div>
<input
name='pets'
defaultValue={contact.pets}
className='mb-2 w-80 rounded-lg border border-gray-300 pl-1'
onChange={handleInputChange}
/>
</Box>
{/* NOTES */}
<Box>
Expand Down Expand Up @@ -266,10 +202,3 @@ const Box = tw.div`
py-1
break-words
`

const PetContainer = tw.div`
px-2
my-2
bg-white
rounded-2xl
`
21 changes: 3 additions & 18 deletions src/components/Contact/contactinfo.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -99,19 +99,11 @@ function ContactInfo({ contact, image }: ContactInfoProps) {
))}
</TagHolder>
</Box>
<Box>
<label htmlFor='pets' className='text-dark-red'>
<Box className='flex flex-col'>
<label htmlFor={contact.pets} className='text-dark-red'>
Pets
</label>
<span className='text-xl'>
{contact.pets.map((pet, index) => (
<div key={index}>
<PetContainer>
<p className='text-dark-red'>{pet.name}</p> {pet.notes}
</PetContainer>
</div>
))}
</span>
<span className='text-xl'>{contact.pets}</span>
</Box>
{/* NOTES */}
<Box className='flex flex-col'>
Expand Down Expand Up @@ -154,10 +146,3 @@ const Tag = tw.div`
text-white
rounded-2xl
`

const PetContainer = tw.div`
px-2
my-2
bg-white
rounded-2xl
`
8 changes: 1 addition & 7 deletions src/components/Contact/contactitem.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -8,12 +8,6 @@ type ContactItemProps = {
}

const ContactItem = ({ contact, image }: ContactItemProps) => {
const petNames = contact.pets
.map((pet) => {
return pet.name
})
.join(', ')

return (
<li className='flex items-center justify-between truncate border-b border-gray-300 bg-white p-3 px-5 text-sm hover:bg-gray-300 focus:bg-gray-300 sm:py-4'>
{/* USER PROFILE IMAGE */}
Expand All @@ -23,7 +17,7 @@ const ContactItem = ({ contact, image }: ContactItemProps) => {
{truncateText(`${contact.firstName} ${contact.lastName}`, 24)}
</p>
</span>
<p className='text-gray-500'>{truncateText(petNames, 16)}</p>
<p className='text-gray-500'>{truncateText(contact.pets, 16)}</p>
</li>
)
}
Expand Down
Original file line number Diff line number Diff line change
@@ -1,15 +1,19 @@
type SearchTagProps = {
name: string
options: string[]
onChangehandler: (event: React.ChangeEvent<HTMLSelectElement>) => void
}

const SearchTag = ({ options, onChangehandler }: SearchTagProps) => {
const SearchTag = ({ name, options, onChangehandler }: SearchTagProps) => {
return (
<div>
<select
onChange={onChangehandler}
className='h-10 border-r border-none bg-transparent pr-10 text-sm focus:outline-none'
className='h-10 border-r border-none bg-transparent pl-2 pr-8 text-sm valid:text-black invalid:text-[#9ca3af] focus:outline-none'
>
<option value='' disabled>
{name}
</option>
<option value=''>All</option>
{options.map((o) => {
return (
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,10 +5,10 @@ type ContactBoxProps = {
function SearchBar({ onChangeHandler }: ContactBoxProps) {
return (
<input
className='h-10 w-full rounded-lg border-none bg-transparent text-sm focus:outline-none'
className='h-10 w-full rounded-lg border-none bg-transparent pl-2 text-sm focus:outline-none'
type='search'
name='search'
placeholder='Search'
placeholder='Search...'
onChange={onChangeHandler}
/>
)
Expand Down
42 changes: 25 additions & 17 deletions src/pages/contact/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -7,8 +7,8 @@ import ContactList from '@/components/Contact/contactlist'
import ProfileItem from '@/components/Contact/profileitem'
import Header from '@/components/Header'
import NavBar from '@/components/NavBar'
import SearchBar from '@/components/SearchBar/searchbar'
import SearchTag from '@/components/SearchBar/searchtag'
import SearchBar from '@/components/SearchBar'
import SearchTag from '@/components/SearchBar/SearchTag'
import type { Contact } from '@/types/types'
// import { withProtected } from '@/components/PrivateRoute

Expand All @@ -17,25 +17,27 @@ const tags = CONTACT_DATA.map((contact) => {
return contact.tags
}).flat()
const set = new Set(tags)
const taglist = [...set]
const tagFilter = [...set]

const Contact = () => {
const [filteredContacts, setFilteredContacts] =
useState<Contact[]>(CONTACT_DATA)

const [selectedOption, setSelectedOption] = useState('')
const [searchFieldString, setSearchFieldString] = useState('')
const [selectedTag, setSelectedTag] = useState<string>('')
const [searchFieldString, setSearchFieldString] = useState<string>('')

function filterContact(includes: string, searchField: string) {
function filterContact(tagf: string, searchField: string) {
const filteredContacts = CONTACT_DATA.filter((contact) => {
const full_name = contact.firstName + ' ' + contact.lastName
if (includes == '') {
return full_name.toLocaleLowerCase().includes(searchField)
}
const filtered =
full_name.toLocaleLowerCase().includes(searchField) &&
contact.tags.some((v) => v.includes(includes))
return filtered

// start for string, comma, or whitspace = word start, ignores case
const reg = new RegExp(`(^|\\s|,)${searchField}`, 'gi')

// don't show contacts without selected tag if tag selected
if (tagf !== '' && !contact.tags.some((v) => v.includes(tagf)))
return false

return reg.test(full_name) || reg.test(contact.pets)
})
setFilteredContacts(filteredContacts)
}
Expand All @@ -44,12 +46,12 @@ const Contact = () => {
const searchFieldString = event.target.value.toLocaleLowerCase()
setSearchFieldString(searchFieldString)
// TODO: Get contact data from server
filterContact(selectedOption, searchFieldString)
filterContact(selectedTag, searchFieldString)
}

const onSearchTagChange = (event: React.ChangeEvent<HTMLSelectElement>) => {
const option_value = event.target.value
setSelectedOption(option_value)
setSelectedTag(option_value)
filterContact(option_value, searchFieldString)
}

Expand All @@ -67,13 +69,19 @@ const Contact = () => {

<div className='m-auto max-w-md'>
<div className='m-2 flex flex-row rounded-xl border-2 border-gray-300'>
<SearchTag options={taglist} onChangehandler={onSearchTagChange} />
<SearchTag
name='Filter By'
options={tagFilter}
onChangehandler={(e: React.ChangeEvent<HTMLSelectElement>) =>
onSearchTagChange(e)
}
/>
<div className='flex w-full justify-between'>
<SearchBar onChangeHandler={onSearchChange} />
<SearchIcon className='my-auto mx-2 h-6' />
</div>
</div>
{searchFieldString === '' && selectedOption === '' && (
{searchFieldString === '' && selectedTag === '' && (
<ProfileItem profile={PROFILE_DATA} image='' />
)}
<ContactList contacts={filteredContacts} />
Expand Down
9 changes: 2 additions & 7 deletions src/types/types.ts
Original file line number Diff line number Diff line change
@@ -1,14 +1,9 @@
interface Pet {
id: string
name: string
notes: string
}
interface Contact {
id: string
firstName: string
lastName: string
desc: string
pets: Pet[]
pets: string
email: string
phone: string
streetAddress: string
Expand All @@ -17,4 +12,4 @@ interface Contact {
tags: string[]
}

export type { Contact, Pet }
export type { Contact }