diff --git a/src/components/common/Spinner.css b/src/components/common/Spinner.css
new file mode 100644
index 0000000..07c1c0b
--- /dev/null
+++ b/src/components/common/Spinner.css
@@ -0,0 +1,42 @@
+/* via https://projects.lukehaas.me/css-loaders/ */
+.loader,
+.loader:after {
+ border-radius: 50%;
+ width: 10em;
+ height: 10em;
+}
+.loader {
+ margin: 60px auto;
+ font-size: 10px;
+ position: relative;
+ text-indent: -9999em;
+ border-top: 1.1em solid rgba(255, 151, 0, 0.2);
+ border-right: 1.1em solid rgba(255, 151, 0, 0.2);
+ border-bottom: 1.1em solid rgba(255, 151, 0, 0.2);
+ border-left: 1.1em solid #ff9700;
+ -webkit-transform: translateZ(0);
+ -ms-transform: translateZ(0);
+ transform: translateZ(0);
+ -webkit-animation: load8 1.1s infinite linear;
+ animation: load8 1.1s infinite linear;
+}
+@-webkit-keyframes load8 {
+ 0% {
+ -webkit-transform: rotate(0deg);
+ transform: rotate(0deg);
+ }
+ 100% {
+ -webkit-transform: rotate(360deg);
+ transform: rotate(360deg);
+ }
+}
+@keyframes load8 {
+ 0% {
+ -webkit-transform: rotate(0deg);
+ transform: rotate(0deg);
+ }
+ 100% {
+ -webkit-transform: rotate(360deg);
+ transform: rotate(360deg);
+ }
+}
diff --git a/src/components/common/Spinner.js b/src/components/common/Spinner.js
new file mode 100644
index 0000000..a8989d2
--- /dev/null
+++ b/src/components/common/Spinner.js
@@ -0,0 +1,8 @@
+import React from "react";
+import "./Spinner.css";
+
+const Spinner = () => {
+ return
Loading...
;
+};
+
+export default Spinner;
diff --git a/src/components/courses/CoursesPage.js b/src/components/courses/CoursesPage.js
index beea601..0b7cf5c 100644
--- a/src/components/courses/CoursesPage.js
+++ b/src/components/courses/CoursesPage.js
@@ -6,6 +6,7 @@ import PropTypes from "prop-types";
import { bindActionCreators } from "redux";
import CourseList from "./CourseList";
import { Redirect } from "react-router-dom";
+import Spinner from "../common/Spinner";
class CoursesPage extends React.Component {
state = {
@@ -33,16 +34,21 @@ class CoursesPage extends React.Component {
<>
{this.state.redirectToAddCoursePage && }
Courses
+ {this.props.loading ? (
+
+ ) : (
+ <>
+
-
-
-
+
+ >
+ )}
>
);
}
@@ -51,7 +57,8 @@ class CoursesPage extends React.Component {
CoursesPage.propTypes = {
courses: PropTypes.array.isRequired,
authors: PropTypes.array.isRequired,
- actions: PropTypes.object.isRequired
+ actions: PropTypes.object.isRequired,
+ loading: PropTypes.bool.isRequired
};
function mapStateToProps(state) {
@@ -66,7 +73,8 @@ function mapStateToProps(state) {
authorName: author.name
};
}),
- authors: state.authors
+ authors: state.authors,
+ loading: state.apiCallsInProgress > 0
};
}
diff --git a/src/components/courses/ManageCoursePage.js b/src/components/courses/ManageCoursePage.js
index 0dd7235..e1b163a 100644
--- a/src/components/courses/ManageCoursePage.js
+++ b/src/components/courses/ManageCoursePage.js
@@ -6,6 +6,7 @@ import PropTypes from "prop-types";
import { bindActionCreators } from "redux";
import CourseForm from "./CourseForm";
import { newCourse } from "../../../tools/mockData";
+import Spinner from "../common/Spinner";
function ManageCoursePage({ courses, authors, actions, history, ...props }) {
const [course, setCourse] = useState({ ...props.course });
@@ -42,7 +43,9 @@ function ManageCoursePage({ courses, authors, actions, history, ...props }) {
});
}
- return (
+ return authors.length === 0 || course.length === 0 ? (
+
+ ) : (
{
diff --git a/src/redux/actions/courseActions.js b/src/redux/actions/courseActions.js
index 261bac1..d3f6731 100644
--- a/src/redux/actions/courseActions.js
+++ b/src/redux/actions/courseActions.js
@@ -1,5 +1,6 @@
import * as types from "./actionTypes";
import * as courseApi from "../../api/courseApi";
+import { beginApiCall } from "./apiStatusActions";
function loadCoursesSuccess(courses) {
return { type: types.LOAD_COURSES_SUCCESS, courses };
@@ -15,6 +16,7 @@ export function updateCourseSuccess(course) {
export function loadCourses() {
return function (dispatch) {
+ dispatch(beginApiCall());
return courseApi
.getCourses()
.then((courses) => {
@@ -29,6 +31,7 @@ export function loadCourses() {
export function saveCourse(course) {
//eslint-disable-next-line no-unused-vars
return function (dispatch, getState) {
+ dispatch(beginApiCall());
return courseApi
.saveCourse(course)
.then((savedCourse) => {
diff --git a/src/redux/reducers/apiStatusReducer.js b/src/redux/reducers/apiStatusReducer.js
new file mode 100644
index 0000000..b56e888
--- /dev/null
+++ b/src/redux/reducers/apiStatusReducer.js
@@ -0,0 +1,19 @@
+import * as types from "../actions/actionTypes";
+import initialState from "./initialState";
+
+function actionTypeEndsInSuccess(type) {
+ return type.substring(type.length - 8) === "_SUCCESS";
+}
+
+export default function apiCallStatusReducer(
+ state = initialState.apiCallsInProgress,
+ action
+) {
+ if (action.type == types.BEGIN_API_CALL) {
+ return state + 1;
+ } else if (actionTypeEndsInSuccess(action.type)) {
+ return state - 1;
+ }
+
+ return state;
+}
diff --git a/src/redux/reducers/index.js b/src/redux/reducers/index.js
index 5ecd3dc..beef339 100644
--- a/src/redux/reducers/index.js
+++ b/src/redux/reducers/index.js
@@ -1,10 +1,12 @@
import { combineReducers } from "redux";
import courseReducer from "./courseReducer";
import authorReducer from "./authorReducer";
+import apiStatusReducer from "./apiStatusReducer";
const rootReducer = combineReducers({
courses: courseReducer,
- authors: authorReducer
+ authors: authorReducer,
+ apiCallsInProgress: apiStatusReducer
});
export default rootReducer;
diff --git a/src/redux/reducers/initialState.js b/src/redux/reducers/initialState.js
index 8d2e4a3..5ca860a 100644
--- a/src/redux/reducers/initialState.js
+++ b/src/redux/reducers/initialState.js
@@ -1,4 +1,5 @@
export default {
courses: [],
- authors: []
+ authors: [],
+ apiCallsInProgress: 0
};