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
2 changes: 1 addition & 1 deletion src/components/FullscreenWrapper.module.scss
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
.wrapper {
width: 100%;
height: 100vh;
height: 100%;
padding: 70px 30px 0;

color: var(--color-black);
Expand Down
37 changes: 37 additions & 0 deletions src/components/Select.jsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
import PropTypes from "prop-types";
import classnames from "classnames";

function Select({ name, disabled, className, options, value }) {
return (
<select
name={name}
disabled={disabled}
className={className}
value={value}
>
{
options.map((option) => (
<option value={option}>
{option}
</option>
))
}
</select>
);
}

Select.propTypes = {
name: PropTypes.string.isRequired,
disabled: PropTypes.bool,
className: PropTypes.string,
options: PropTypes.array,
};

Select.defaultProps = {
name: "Select",
disabled: false,
className: null,
options: [],
};

export default Select;
1 change: 1 addition & 0 deletions src/components/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -3,3 +3,4 @@ export { default as CustomLink } from "components/CustomLink";
export { default as FullscreenWrapper } from "components/FullscreenWrapper";
export { default as Input } from "components/Input";
export { default as Loader } from "components/Loader";
export { default as Select } from "components/Select";
6 changes: 6 additions & 0 deletions src/constants/ActionTypes.js
Original file line number Diff line number Diff line change
Expand Up @@ -7,3 +7,9 @@ export const auth = keyMirror({

LOGOUT: null,
});

export const dog = keyMirror({
FETCH_DOG_PENDING: null,
FETCH_DOG_COMPLETED: null,
FETCH_DOG_FAILED: null,
});
20 changes: 20 additions & 0 deletions src/global.css
Original file line number Diff line number Diff line change
@@ -1,3 +1,23 @@
.thumb {
width: 250px;
height: 250px;
}

.small {
width: 500px;
height: 500px;
}

.med {
width: 750px;
height: 750px;
}

.full {
width: 1000px;
height: 1000px;
}

.m-all-1 {
margin: var(--space-1);
}
Expand Down
131 changes: 126 additions & 5 deletions src/pages/Home.jsx
Original file line number Diff line number Diff line change
@@ -1,17 +1,138 @@
import { FullscreenWrapper, CustomLink } from "components";
import { useState, useEffect } from "react";
import { useSelector, useDispatch } from "react-redux";

import { FullscreenWrapper, CustomLink, Loader, Button, Input, Select } from "components";
import { logout } from "redux/actions/authActions";

import { fetchDogs } from "redux/actions/dogActions";
import { waitForDomChange } from "@testing-library/react";

function Home() {
const userName = null;
const username = useSelector(s => s.auth.username);
const fetchingDogs = useSelector(s => s.dog.fetchingDogs);
const dogsList = useSelector(s => s.dog.dogsList);

const dispatch = useDispatch();

const [currentPage, setCurrentPage] = useState(0);
const [photoLimit, setPhotoLimit] = useState(10);
const [ascending, setAscending] = useState(true);
const [selection, setSelection] = useState("thumb");

useEffect(function onMount(){
if (username){
dispatch(fetchDogs(
{
page: currentPage,
limit: photoLimit,
order: ascending ? "ASC" : "DESC"
}
));
}
}, [currentPage, ascending, selection]);

function handleValueChange(e, valueCb) {
const newValue = e.target.value;
valueCb(newValue);
}

function handleCurrentPageDecrement() {
if (currentPage === 0) {
return;
}

setCurrentPage(currentPage -1);
}

function handleCurrentPageIncrement() {
setCurrentPage(currentPage +1);
}

function handleOrderChange() {
setAscending(!ascending);
}

function handleLogout() {
dispatch(logout());
}

function handleSelectChange(e, valueCb) {
console.log("selection changed")
const newValue = e.target.value;
valueCb(newValue);
}

return (
<FullscreenWrapper>
<h1>Doggo List</h1>
<p className="m-bottom-4">
{userName
? `Welcome, ${userName}!`
{username
? `Welcome, ${username}!`
: "To see a list of doggos, please log in first."}
</p>
<CustomLink to="login" label="Log In" />
{username ? (
<div className="m-bottom-4">
<Button
label="<"
className="m-right-1"
disabled={fetchingDogs}
onClick={handleCurrentPageDecrement}
/>
<Button label={currentPage} />
<Button
label=">"
className="m-left-1"
disabled={fetchingDogs}
onClick={handleCurrentPageIncrement}
/>
<div>
<Input
type="text"
name="limit"
placeholder="Photo limit"
className="m-all-2"
onChange={(e) => handleValueChange(e, setPhotoLimit)}
/>

<select
onChange={(e) => handleSelectChange(e, setSelection)}
action="/"
>
<option selected value="thumb" key="thumb">thumb</option>
<option value="small" key="small">small</option>
<option value="med" key="med">med</option>
<option value="full" key="full">full</option>
</select>

<Button
label={ascending ? "ASC" : "DESC"}
className="m-all-1"
disabled={fetchingDogs}
onClick={handleOrderChange}
/>
</div>
<Button
label="Logout"
className="m-all-1"
onClick={handleLogout}
/>
</div>
) : (<CustomLink to="login" label="Log In" />)
}
{username ? ((fetchingDogs) ? <Loader /> : (
<div>
{
dogsList.map((dog) => (
<div className="m-bottom-4">
<img className={selection} src={dog.url} />
<label>
{dog.breeds[0] ? dog.breeds[0].name : "Doggo"}
</label>
</div>
))
}
</div>
)): null}
</FullscreenWrapper>
);
}
Expand Down
5 changes: 5 additions & 0 deletions src/pages/Login.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,11 @@ function Login() {
function handleFormSubmit(e) {
e.preventDefault();
console.log(name, password);
if(!name || !password){
alert("Name and Password required");
}else{
dispatch(login(name));
}
// dispatch(login());
}

Expand Down
14 changes: 4 additions & 10 deletions src/redux/actions/authActions.js
Original file line number Diff line number Diff line change
@@ -1,26 +1,20 @@
import { auth } from "constants/ActionTypes";

export function login() {
export function login(username) {
return async (dispatch) => {
dispatch({
type: auth.LOGIN_PENDING,
});

const hasError = false;
setTimeout(() => {
if (hasError) {
dispatch({
type: auth.LOGIN_FAILED,
type: auth.LOGIN_COMPLETED,
payload: {
errorMessage: "Something went wrong, please try again later.",
username,
},
});
} else {
dispatch({
type: auth.LOGIN_COMPLETED,
});
}
}, 3000);
}, 0);
};
}

Expand Down
24 changes: 24 additions & 0 deletions src/redux/actions/dogActions.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
import { dog } from "constants/ActionTypes";

import { default as fetchDogsAPI } from "utils/fetchDogs";

export function fetchDogs({page = 0,limit = 10, order = "ASC"} = {}){
return async (dispatch) => {
dispatch({
type: dog.FETCH_DOG_PENDING,
});

const dogsList = await fetchDogsAPI({
page,
limit,
order,
});

dispatch({
type: dog.FETCH_DOG_COMPLETED,
payload: {
dogsList,
},
})
};
}
4 changes: 4 additions & 0 deletions src/redux/reducers/authReducer.js
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ const initialState = {
loginError: null, // Wrong credentials / sorry you can't login today

isLoggedIn: false,
username: null,
};

/**
Expand All @@ -22,10 +23,12 @@ export default function reducer(state = initialState, action = {}) {
}

case auth.LOGIN_COMPLETED: {
const { username } = action.payload;
return {
...state,
loginPending: false,
isLoggedIn: true,
username,
};
}

Expand All @@ -42,6 +45,7 @@ export default function reducer(state = initialState, action = {}) {
return {
...state,
isLoggedIn: false,
username: null,
};
}

Expand Down
33 changes: 33 additions & 0 deletions src/redux/reducers/dogReducer.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
import { dog } from "constants/ActionTypes";

const initialState = {
fetchingDogs: false,
dogsList: [],
};

export default function reducer(state = initialState, action = {}) {
switch(action.type){
case dog.FETCH_DOG_PENDING: {
return {
...state,
fetchingDogs: true,
}
}
case dog.FETCH_DOG_COMPLETED: {
const { dogsList} = action.payload;
return {
...state,
fetchingDogs: false,
dogsList,
}
}
case dog.FETCH_DOG_FAILED: {
return {
...state,
fetchingDogs: false,
}
}
default:
return state
}
}
2 changes: 2 additions & 0 deletions src/redux/reducers/index.js
Original file line number Diff line number Diff line change
@@ -1,9 +1,11 @@
import { combineReducers } from "redux";

import auth from "redux/reducers/authReducer";
import dog from "redux/reducers/dogReducer";

const rootReducer = combineReducers({
auth,
dog,
});

export default rootReducer;
2 changes: 1 addition & 1 deletion src/utils/fetchDogs.js
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ async function fetchDogs({
limit = 10,
page = 0,
order = "ASC",
size = "med",
size = "small",
} = {}) {
const response = await fetch(
`https://api.thedogapi.com/v1/images/search?limit=${limit}&page=${page}&order=${order}&size=${size}`
Expand Down