Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
15 changes: 1 addition & 14 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,16 +1,3 @@
# Frontend developer test

Run `yarn install` to install all dependencies
you will find all instructions for this task within the homepage when you first run `yarn start`

## Available Scripts

In the project directory, you can run:

### `yarn start`

Runs the app in the development mode.\
Open [http://localhost:3000](http://localhost:3000) to view it in the browser.

The page will reload if you make edits.\
You will also see any lint errors in the console.
The test took me roughly 1 hour to complete. I created a custom axios/fetch hook so i could use repeatable code to fetch data without repeating it. The page is fully responsive. I've also added a search function so the user can filter through the data. You can click any card which directs you to a page with more information about user.
16,332 changes: 16,332 additions & 0 deletions package-lock.json

Large diffs are not rendered by default.

2 changes: 2 additions & 0 deletions src/App.js
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ import Users from './views/Users';
import Navbar from './components/Navbar';

import './app.scss';
import Profile from './components/Profile';

function App() {
return (
Expand All @@ -13,6 +14,7 @@ function App() {
<Switch>
<Route path="/" component={Home} exact />
<Route path="/users" component={Users} />
<Route path="/:id" component={Profile} />
</Switch>
</div>
);
Expand Down
46 changes: 46 additions & 0 deletions src/components/Profile/Profile.jsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,46 @@
import React from "react";
import { useParams } from "react-router";
import useAxios from "../../useAxios";
import { useHistory } from "react-router";
import styles from "./Profile.module.scss";

const Profile = () => {
const { id } = useParams();
const history = useHistory();

// custom axios hook
const { data, error, isLoading } = useAxios(
"https://jsonplaceholder.typicode.com/users/" + id
);

return (
<div className={styles.profile}>
{error && <p>{error}</p>}
{isLoading && <p>{isLoading}</p>}
<h1>{data.name}'s Profile</h1>
<div>
<h4>userName:</h4>
<p>{data.username}</p>
</div>
<div>
<h4>Phone Number:</h4>
<p>{data.phone}</p>
</div>
<div>
<h4>Adress:</h4>
{data.address && (
<p>
{data.address.suite}, {data.address.street}, {data.address.city}
</p>
)}
</div>
<div>
<h4>Company:</h4>
{data.company && <p>{data.company.name}</p>}
</div>
<button onClick={history.goBack}>Go Back</button>
</div>
);
};

export default Profile;
36 changes: 36 additions & 0 deletions src/components/Profile/Profile.module.scss
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
.profile {
text-align: center;

div {
display: flex;
justify-content: center;
align-items: center;

h4 {
margin-right: 10px;
}
}

button {
background: rgb(70, 52, 52);
color: #fff;
border: 0;
padding: 10px;
margin-bottom: 10px;
border-radius: 8px;
cursor: pointer;
opacity: 0.8;

&:hover {
opacity: 1;
}
}
}

@media screen and (max-width: 520px) {
.profile {
div {
display: block;
}
}
}
3 changes: 3 additions & 0 deletions src/components/Profile/index.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
import Profile from "./Profile";

export default Profile
37 changes: 37 additions & 0 deletions src/components/User/User.jsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
import React, { useState } from "react";
import styles from "./User.module.scss";
import { Link } from "react-router-dom";

const User = ({ data }) => {
const [inputName, setInputName] = useState("");

// filters data through user search input and returns desired data
const filteredSearch = data.filter((person) => {
return person.name
.toLowerCase()
.includes(inputName.toLocaleLowerCase().trim());
});

return (
<div className={styles.container}>
<div className={styles.container__search}>
<h3>Search Name</h3>
<input
type="text"
value={inputName}
onChange={(e) => setInputName(e.target.value)}
/>
</div>

<div className={styles.container__user}>
{filteredSearch.map((person) => (
<Link className={styles.button} to={`/${person.id}`}>
<h4>{person.name}</h4>
</Link>
))}
</div>
</div>
);
};

export default User;
72 changes: 72 additions & 0 deletions src/components/User/User.module.scss
Original file line number Diff line number Diff line change
@@ -0,0 +1,72 @@
.container {

.container__search {
display: flex;
justify-content: center;
align-items: center;

input {
padding-top: 10px;
padding-bottom: 10px;
background-color: lightgray;
border-radius: 5px;
font-size: 14px;
height: 18px;
margin-left: 20px;
}
}

.container__user {
display: grid;
grid-template-columns: repeat(6, 1fr);

.button {
background-color: darkgray;
margin: 20px;
border-radius: 10px;
max-width: 200px;
text-align: center;
opacity: 0.8;
text-decoration: none;
list-style-type: none;
text-decoration: none;
color: black;

&:hover {
opacity: 1;
}
}
}
}

@media screen and (max-width: 1200px) {
.container {
.container__user {
grid-template-columns: repeat(5, 1fr);
}
}
}

@media screen and (max-width: 970px) {
.container {
.container__user {
grid-template-columns: repeat(4, 1fr);
}
}
}

@media screen and (max-width: 820px) {
.container {
.container__user {
grid-template-columns: repeat(3, 1fr);
}
}
}

@media screen and (max-width: 520px) {
.container {
.container__user {
grid-template-columns: repeat(2, 1fr);
}
}
}
8 changes: 2 additions & 6 deletions src/components/User/index.js
Original file line number Diff line number Diff line change
@@ -1,7 +1,3 @@
import React from 'react';
import User from "./User";

const User = () => (
<></>
)

export default User;
export default User
1 change: 0 additions & 1 deletion src/logo.svg

This file was deleted.

31 changes: 31 additions & 0 deletions src/useAxios.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
import { useState, useEffect } from "react"

import axios from 'axios';

const useAxios = (url) => {
const [isLoading, setIsLoading] = useState(true);
const [data, setData] =useState([])
const [error, setError] = useState(false)

useEffect(() => {
axios.get(url)
.then(response => {
console.log(response.data);
setData(response.data)
setIsLoading(false)
setError(false)
})
.catch(err => {
if (err.message === 'AboutError') {
console.log('fetch aborted');
} else {
setIsLoading(false)
setError(err.message)
}
})
}, [url]);

return {data, isLoading, error}
}

export default useAxios
27 changes: 13 additions & 14 deletions src/views/Home/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -7,39 +7,38 @@ const Home = () => {
<div className="home">
<div className="container">
<div className="task">
<h1>Frontend developer test</h1>
<h1>Zak's Frontend developer test</h1>

<strong>Estimated time</strong>
<p>We expect the requirements to take around 1 - 2 hours to complete but feel free to have fun with it!</p>
<p>The test took me about 1 hour (plus 15 minutes of procrasination)</p>
<p>I didn't know what to do with this page so i decided to put extra information on how i found the test</p>

<strong>Background info</strong>
<p>We are using <a href="https://jsonplaceholder.typicode.com/">JSON Placeholder</a> to grab test data for this so it may be worth having a read about what api endpoints are available.</p>
<p>We are using <a href="https://axios-http.com/">Axios</a> to do the base API request found inside views/Users</p>
<p>I created a custom fetch/axios hook called useAxios.js in src folder so i can reuse the fetch code without having to type the same block of code again</p>


<strong>Task</strong>

<p>Inside views/Users you will find a request to get an array of objects for users. We currently just console log out the data we get back. Use this data to output individual User components in a styled list / grid</p>
<p>A user component should contain a user's basic details with a link through to their profile. We'll leave this down to your discretion on style.</p>
<p>As stated above, each user component should link through to a profile page. This profile will also need to be created and hooked up using React Router logic so that they are dynamic routes for each user. This should show additional information for that specific selected user.</p>
<p>Page is fully responsive. </p>
<p>A Search is added so the user can filter through the data via searches</p>


<strong>Requirements</strong>
<ul>
<li>Needs to be responsive</li>
<li>Code should be commented where it makes sense to do so or should include a readMe</li>
<li>Follow best practices</li>
<li>Needs to be responsive</li>
<li>Code should be commented where it makes sense to do so or should include a readMe</li>
<li>Follow best practices</li>
</ul>

<strong>Bonus</strong>
<ul>
<li>
Add back button from user profile
Add back button from user profile
</li>
<li>
Add simple search functionality to list of users
Add simple search functionality to list of users
</li>
<li>
Improve the original code / directory structure
Improve the original code / directory structure (subject to interpretation but i think ✓)
</li>
</ul>
</div>
Expand Down
30 changes: 11 additions & 19 deletions src/views/Users/index.js
Original file line number Diff line number Diff line change
@@ -1,25 +1,17 @@
import React, { useEffect, useState } from 'react';

import axios from 'axios';

//import User from '../../components/User';
import React from 'react';
import User from '../../components/User';
import useAxios from '../../useAxios';

const Users = () => {
const [isLoading, setIsLoading] = useState(true);

useEffect(() => {
axios.get('https://jsonplaceholder.typicode.com/users')
.then(response => {
// grab this data / loop through and output individual User component for each object
console.log(response.data);
})
.catch(err => {
// Do something
})
}, []);
// custom axios hook
const {data, error, isLoading} = useAxios('https://jsonplaceholder.typicode.com/users')

return isLoading && (
<h1>Loading...</h1>
return (
<div>
{ isLoading && <h1>Loading...</h1>}
{ error && <div>{error }</div> }
<User data={data} />
</div>
)
}

Expand Down