Skip to content

Commit

Permalink
Merge pull request #88 from codersforcauses/i86-search_by_pet_names_i…
Browse files Browse the repository at this point in the history
…n_contacts_page

I86 search by pet names in contacts page
  • Loading branch information
swolrus committed Jul 21, 2022
2 parents 95c1272 + 2ae04a6 commit 5d3e098
Show file tree
Hide file tree
Showing 9 changed files with 58 additions and 888 deletions.
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 }

1 comment on commit 5d3e098

@vercel
Copy link

@vercel vercel bot commented on 5d3e098 Jul 21, 2022

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Successfully deployed to the following URLs:

poops – ./

poops.vercel.app
poops-git-main-coders-for-causes.vercel.app
poops-coders-for-causes.vercel.app

Please sign in to comment.