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

Completed the assessment, all tests pass. #4

Open
wants to merge 1 commit into
base: main
Choose a base branch
from
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: 9 additions & 6 deletions src/features/photos/create/index.js
Original file line number Diff line number Diff line change
@@ -1,13 +1,15 @@
import { useState } from 'react';

// Task 2: Import the `useDispatch()` method from the appropriate package
// Task 3: Import the `addPhoto()` action creator from the photos slice

// Task 2: √ Import the `useDispatch()` method from the appropriate package
import { useDispatch } from 'react-redux';
// Task 3: Import the `addPhoto()` action creator from the photos slice
import { addPhoto } from '../photos.slice';
import './create.css';


export default function CreatePhoto() {
const [formData, setFormData] = useState({ imageUrl: '', caption: '' });
// Task 4: Store a reference to the Redux store's dispatch method in a variable called `dispatch`
// Task 4: √ Store a reference to the Redux store's dispatch method in a variable called `dispatch`
const dispatch = useDispatch();

function handleChange({ target: { name, value } }) {
setFormData({
Expand All @@ -18,7 +20,8 @@ export default function CreatePhoto() {

function handleSubmit(event) {
event.preventDefault();
// Task 5: Dispatch the `addPhoto()` action creator, passing in the form data
// Task 5: √ Dispatch the `addPhoto()` action creator, passing in the form data
dispatch(addPhoto(formData));
setFormData({ imageUrl: '', caption: '' });
}

Expand Down
16 changes: 10 additions & 6 deletions src/features/photos/list/index.js
Original file line number Diff line number Diff line change
@@ -1,18 +1,22 @@
import { useSelector, useDispatch } from 'react-redux';
import {
// Task 7: Import the `removePhoto()` action creator from the photos slice
// Task 7: √ Import the `removePhoto()` action creator from the photos slice
removePhoto,
selectAllPhotos,
// Task 13: Import the `selectFilteredPhotos()` selector from the photos slice
// Task 13: √ Import the `selectFilteredPhotos()` selector from the photos slice
selectFilteredPhotos,
} from '../photos.slice';
import './list.css';

export default function PhotosList() {
// Task 14: Call `useSelector()` below with `selectFilteredPhotos` instead of `selectAllPhotos`
const photos = useSelector(selectAllPhotos);
// Task 8: Store a reference to the Redux store's dispatch method in a variable called `dispatch`
// Task 14: √ Call `useSelector()` below with `selectFilteredPhotos` instead of `selectAllPhotos`
const photos = useSelector(selectFilteredPhotos);
// Task 8: √ Store a reference to the Redux store's dispatch method in a variable called `dispatch`
const dispatch = useDispatch();

function handleDeleteButtonClick(id) {
// Task 9: Dispatch the `removePhoto()` action creator, passing in the id
// Task 9: √ Dispatch the `removePhoto()` action creator, passing in the id
dispatch(removePhoto(id));
}

const photosListItems = photos.map(({ id, caption, imageUrl }) => (
Expand Down
18 changes: 13 additions & 5 deletions src/features/photos/photos.slice.js
Original file line number Diff line number Diff line change
Expand Up @@ -10,14 +10,19 @@ const options = {
name: 'photos',
initialState,
reducers: {
// Task 1: Create an `addPhoto()` case reducer that adds a photo to state.photos.
// Task 1: Create an `addPhoto()` case reducer that adds a photo to state.photos.
// Task 1 Hint: You can use state.photos.unshift()
// `unshift()` documentation: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/unshift

// Task 6: Create an `removePhoto()` case reducer that removes a photo from state.photos
addPhoto: (state, action) => {
state.photos.unshift({id: state.photos.length + 1, ...action.payload});
},
// Task 6: √ Create an `removePhoto()` case reducer that removes a photo from state.photos
// Task 6 Hint: You can use state.photos.splice()
// `splice()` documentation: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/splice
},
removePhoto: (state, action) => {
state.photos.splice(state.photos.findIndex((photo) => photo.id === action.payload), 1);
},
}
};

const photosSlice = createSlice(options);
Expand All @@ -28,5 +33,8 @@ export default photosSlice.reducer;

export const selectAllPhotos = (state) => state.photos.photos;
export const selectFilteredPhotos = (state) => {
// Task 12: Complete `selectFilteredPhotos()` selector to return a filtered list of photos whose captions match the user's search term
// Task 12: √ Complete `selectFilteredPhotos()` selector to return a filtered list of photos whose captions match the user's search term
const photos = selectAllPhotos(state);
const searchTerm = selectSearchTerm(state);
return photos.filter((photo) => photo.caption?.toLowerCase().includes(searchTerm.toLowerCase()));
};
6 changes: 4 additions & 2 deletions src/features/search/search-bar/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -4,10 +4,12 @@ import './search-bar.css';

export default function SearchBar() {
const searchTerm = useSelector(selectSearchTerm);
// Task 10: Store a reference to the Redux store's dispatch method in a variable called `dispatch`
// Task 10: √ Store a reference to the Redux store's dispatch method in a variable called `dispatch`
const dispatch = useDispatch();

function handleChange({ target: { value } }) {
// Task 11: Dispatch the `setSearchTerm()` action creator, passing in the value of the search input
// Task 11: √ Dispatch the `setSearchTerm()` action creator, passing in the value of the search input
dispatch(setSearchTerm(value));
}

return (
Expand Down
19 changes: 11 additions & 8 deletions src/features/suggestion/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -4,20 +4,23 @@ import {
fetchSuggestion,
selectError,
selectLoading,
// Task 18: Import the `selectSuggestion()` selector from the suggestion slice
// Task 18: √ Import the `selectSuggestion()` selector from the suggestion slice
selectSuggestion,
} from './suggestion.slice';
import './suggestion.css';

export default function Suggestion() {
// Task 19: Call useSelector() with the selectSuggestion() selector
// Task 19: √ Call useSelector() with the selectSuggestion() selector
const { imageUrl, caption } = useSelector(selectSuggestion);
// The component needs to access the `imageUrl` and `caption` properties of the suggestion object.
const loading = useSelector(selectLoading);
const error = useSelector(selectError);
const dispatch = useDispatch();

useEffect(() => {
async function loadSuggestion() {
// Task 20: Dispatch the fetchSuggestion() action creator
// Task 20: √ Dispatch the fetchSuggestion() action creator
await dispatch(fetchSuggestion());
}
loadSuggestion();
}, [dispatch]);
Expand All @@ -28,12 +31,12 @@ export default function Suggestion() {
} else if (error) {
render = <h3>Sorry, we're having trouble loading the suggestion.</h3>;
} else {
// Task 21: Enable the two JSX lines below needed to display the suggestion on the page
// Task 21: Enable the two JSX lines below needed to display the suggestion on the page
render = (
<>
{/* <img alt={caption} src={imageUrl} />
<p>{imageUrl}</p> */}
</>
<>
<img alt={caption} src={imageUrl}/>
<p>{imageUrl}</p>
</>
);
}

Expand Down
30 changes: 26 additions & 4 deletions src/features/suggestion/suggestion.slice.js
Original file line number Diff line number Diff line change
@@ -1,7 +1,16 @@
import { createAsyncThunk, createSlice } from '@reduxjs/toolkit';

export const fetchSuggestion =
createAsyncThunk(/* Task 15: Complete the `createAsyncThunk()` function to load a suggestion from this URL: http://localhost:3004/api/suggestion */);
createAsyncThunk(
/* Task 15: √ Complete the `createAsyncThunk()` function to load a suggestion from this URL: http://localhost:3004/api/suggestion */
'suggestion/fetchSuggestion',
async (arg, thunkAPI) => {
const response = await fetch('http://localhost:3004/api/suggestion');
const { data } = await response.json();
// console.log(data);
return data;
}
);

const initialState = {
suggestion: '',
Expand All @@ -14,15 +23,28 @@ const options = {
initialState,
reducers: {},
extraReducers: {
/* Task 16: Inside `extraReducers`, add reducers to handle all three promise lifecycle states - pending, fulfilled, and rejected - for the `fetchSuggestion()` call */
/* Task 16: √ Inside `extraReducers`, add reducers to handle all three promise lifecycle states - pending, fulfilled, and rejected - for the `fetchSuggestion()` call */
[fetchSuggestion.pending]: (state) => {
state.loading = true;
state.error = false;
},
[fetchSuggestion.fulfilled]: (state, { payload: { imageUrl, caption } }) => {
state.suggestion = { imageUrl, caption };
state.loading = false;
state.error = false;
},
[fetchSuggestion.rejected]: (state, action) => {
state.loading = false;
state.error = true;
},
},
};

const suggestionSlice = createSlice(options);

export default suggestionSlice.reducer;

// Task 17: Create a selector, called `selectSuggestion`, for the `suggestion` state variable and export it from the file

// Task 17: Create a selector, called `selectSuggestion`, for the `suggestion` state variable and export it from the file
export const selectSuggestion = (state) => state.suggestion.suggestion;
export const selectLoading = (state) => state.suggestion.loading;
export const selectError = (state) => state.suggestion.error;