├── .gitignore
├── README.md
├── craco.config.js
├── package.json
├── public
├── favicon.ico
├── index.html
├── logo192.png
├── logo512.png
├── manifest.json
└── robots.txt
├── src
├── App.js
├── ContactCards.jsx
├── index.css
├── index.js
├── reportWebVitals.js
└── setupTests.js
└── tailwind.config.js
/.gitignore:
--------------------------------------------------------------------------------
1 | # See https://help.github.com/articles/ignoring-files/ for more about ignoring files.
2 |
3 | # dependencies
4 | /node_modules
5 | /.pnp
6 | .pnp.js
7 |
8 | # testing
9 | /coverage
10 |
11 | # production
12 | /build
13 |
14 | # misc
15 | .DS_Store
16 | .env.local
17 | .env.development.local
18 | .env.test.local
19 | .env.production.local
20 |
21 | npm-debug.log*
22 | yarn-debug.log*
23 | yarn-error.log*
24 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # React Contact List App
2 |
3 | [Here's a link to my tutorial for this project](https://youtu.be/3g42k_JVqI4)
4 |
5 | [Here's a link to the hosted app](https://rclist.netlify.app/)
6 |
--------------------------------------------------------------------------------
/craco.config.js:
--------------------------------------------------------------------------------
1 | module.exports = {
2 | style: {
3 | postcss: {
4 | plugins: [
5 | require('tailwindcss'),
6 | require('autoprefixer'),
7 | ],
8 | },
9 | },
10 | }
11 |
--------------------------------------------------------------------------------
/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "react-contact-list",
3 | "version": "0.1.0",
4 | "private": true,
5 | "dependencies": {
6 | "@craco/craco": "^6.1.1",
7 | "@testing-library/jest-dom": "^5.11.4",
8 | "@testing-library/react": "^11.1.0",
9 | "@testing-library/user-event": "^12.1.10",
10 | "react": "^17.0.2",
11 | "react-dom": "^17.0.2",
12 | "react-fetch-hook": "^1.8.5",
13 | "react-scripts": "4.0.3",
14 | "web-vitals": "^1.0.1"
15 | },
16 | "scripts": {
17 | "start": "craco start",
18 | "build": "craco build",
19 | "test": "craco test",
20 | "eject": "react-scripts eject"
21 | },
22 | "eslintConfig": {
23 | "extends": [
24 | "react-app",
25 | "react-app/jest"
26 | ]
27 | },
28 | "browserslist": {
29 | "production": [
30 | ">0.2%",
31 | "not dead",
32 | "not op_mini all"
33 | ],
34 | "development": [
35 | "last 1 chrome version",
36 | "last 1 firefox version",
37 | "last 1 safari version"
38 | ]
39 | },
40 | "devDependencies": {
41 | "@tailwindcss/postcss7-compat": "^2.1.0",
42 | "autoprefixer": "^9.8.6",
43 | "postcss": "^7.0.35",
44 | "tailwindcss": "npm:@tailwindcss/postcss7-compat@^2.1.0"
45 | }
46 | }
47 |
--------------------------------------------------------------------------------
/public/favicon.ico:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ebenezerdon/react-contact-list/a7f4f398eb97415b687075b640ee0003e73a4c0f/public/favicon.ico
--------------------------------------------------------------------------------
/public/index.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
12 |
13 |
17 |
18 |
27 | React App
28 |
29 |
30 |
31 |
32 |
42 |
43 |
44 |
--------------------------------------------------------------------------------
/public/logo192.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ebenezerdon/react-contact-list/a7f4f398eb97415b687075b640ee0003e73a4c0f/public/logo192.png
--------------------------------------------------------------------------------
/public/logo512.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ebenezerdon/react-contact-list/a7f4f398eb97415b687075b640ee0003e73a4c0f/public/logo512.png
--------------------------------------------------------------------------------
/public/manifest.json:
--------------------------------------------------------------------------------
1 | {
2 | "short_name": "React App",
3 | "name": "Create React App Sample",
4 | "icons": [
5 | {
6 | "src": "favicon.ico",
7 | "sizes": "64x64 32x32 24x24 16x16",
8 | "type": "image/x-icon"
9 | },
10 | {
11 | "src": "logo192.png",
12 | "type": "image/png",
13 | "sizes": "192x192"
14 | },
15 | {
16 | "src": "logo512.png",
17 | "type": "image/png",
18 | "sizes": "512x512"
19 | }
20 | ],
21 | "start_url": ".",
22 | "display": "standalone",
23 | "theme_color": "#000000",
24 | "background_color": "#ffffff"
25 | }
26 |
--------------------------------------------------------------------------------
/public/robots.txt:
--------------------------------------------------------------------------------
1 | # https://www.robotstxt.org/robotstxt.html
2 | User-agent: *
3 | Disallow:
4 |
--------------------------------------------------------------------------------
/src/App.js:
--------------------------------------------------------------------------------
1 | import useFetch from 'react-fetch-hook'
2 | import ContactCards from './ContactCards'
3 | import {useEffect, useState} from 'react'
4 |
5 | const App = () => {
6 | const url = 'https://randomuser.me/api/'
7 | const { data } = useFetch(url+'?results=200')
8 | const [contactList, setContactList] = useState()
9 | const [filterQuery, setFilterQuery] = useState()
10 |
11 | useEffect(() => {
12 | if (!filterQuery) {
13 | setContactList(data?.results?.slice(0, 10))
14 | } else {
15 | const queryString = filterQuery.toLowerCase()
16 | const filteredData = data?.results?.filter(contact => {
17 | const fullName = `${contact.name.first} ${contact.name.last}`
18 |
19 | // if it's just one letter, return all names that start with it
20 | if (queryString.length === 1) {
21 | const firstLetter = fullName.charAt(0).toLowerCase()
22 | return firstLetter === queryString
23 | }
24 | else {
25 | return fullName.toLowerCase().includes(queryString)
26 | }
27 | })
28 | setContactList(filteredData)
29 | }
30 | }, [data, filterQuery])
31 |
32 | return (
33 |
34 |
44 |
45 | {contactList?.length < 1 && (
46 | No data matches your search
47 | )}
48 |
49 |
50 |
51 | )
52 | }
53 |
54 | export default App
55 |
--------------------------------------------------------------------------------
/src/ContactCards.jsx:
--------------------------------------------------------------------------------
1 | const ContactCards = ({ contactList }) => {
2 | return (
3 | <>
4 | {contactList?.map((contact, index) => (
5 |
6 |
7 |
8 |
9 | {contact.name.first} {contact.name.last}
10 |
11 |
12 | email: {contact.email}
13 |
14 |
15 | phone: {contact.cell}
16 |
17 |
18 | city: {contact.location.city}
19 |
20 |
21 |
22 | ))}
23 | >
24 | )
25 | }
26 |
27 | export default ContactCards
28 |
--------------------------------------------------------------------------------
/src/index.css:
--------------------------------------------------------------------------------
1 | @tailwind base;
2 | @tailwind components;
3 | @tailwind utilities;
4 |
--------------------------------------------------------------------------------
/src/index.js:
--------------------------------------------------------------------------------
1 | import React from 'react'
2 | import ReactDOM from 'react-dom'
3 | import App from './App'
4 | import './index.css';
5 |
6 | ReactDOM.render(
7 |
8 |
9 | ,
10 | document.getElementById('root')
11 | )
12 |
--------------------------------------------------------------------------------
/src/reportWebVitals.js:
--------------------------------------------------------------------------------
1 | const reportWebVitals = onPerfEntry => {
2 | if (onPerfEntry && onPerfEntry instanceof Function) {
3 | import('web-vitals').then(({ getCLS, getFID, getFCP, getLCP, getTTFB }) => {
4 | getCLS(onPerfEntry);
5 | getFID(onPerfEntry);
6 | getFCP(onPerfEntry);
7 | getLCP(onPerfEntry);
8 | getTTFB(onPerfEntry);
9 | });
10 | }
11 | };
12 |
13 | export default reportWebVitals;
14 |
--------------------------------------------------------------------------------
/src/setupTests.js:
--------------------------------------------------------------------------------
1 | // jest-dom adds custom jest matchers for asserting on DOM nodes.
2 | // allows you to do things like:
3 | // expect(element).toHaveTextContent(/react/i)
4 | // learn more: https://github.com/testing-library/jest-dom
5 | import '@testing-library/jest-dom';
6 |
--------------------------------------------------------------------------------
/tailwind.config.js:
--------------------------------------------------------------------------------
1 | module.exports = {
2 | purge: ['./src/**/*.{js,jsx,ts,tsx}', './public/index.html'],
3 | darkMode: false, // or 'media' or 'class'
4 | theme: {
5 | extend: {},
6 | },
7 | variants: {
8 | extend: {},
9 | },
10 | plugins: [],
11 | }
12 |
--------------------------------------------------------------------------------