diff --git a/admin/src/App.js b/admin/src/App.js
index 89f2dc0..8d030c8 100644
--- a/admin/src/App.js
+++ b/admin/src/App.js
@@ -18,6 +18,11 @@ class App extends Component {
auth
+
+
+ events
+
+
diff --git a/admin/src/components/events/events-list.js b/admin/src/components/events/events-list.js
new file mode 100644
index 0000000..d4bdedd
--- /dev/null
+++ b/admin/src/components/events/events-list.js
@@ -0,0 +1,28 @@
+import React, { Component } from 'react'
+import { connect } from 'react-redux'
+import { eventsSelector } from '../../ducks/events'
+
+class EventsList extends Component {
+ static propTypes = {}
+
+ render() {
+ const { events } = this.props
+ return (
+
+ {Object.keys(events).map((key) => (
+ -
+ {events[key].title}
+
+ Where: {events[key].where}
+
+ When: {events[key].when}
+
+ ))}
+
+ )
+ }
+}
+
+export default connect((state) => ({
+ events: eventsSelector(state)
+}))(EventsList)
diff --git a/admin/src/config.js b/admin/src/config.js
index aa22b3b..9604855 100644
--- a/admin/src/config.js
+++ b/admin/src/config.js
@@ -2,15 +2,15 @@ import firebase from 'firebase/app'
import 'firebase/auth'
import 'firebase/database'
-export const appName = 'advreact-10-05'
+export const appName = 'advreact-js'
export const config = {
- apiKey: 'AIzaSyCbMQM0eQUSQ0SuLVAu9ZNPUcm4rdbiB8U',
+ apiKey: 'AIzaSyBCNW1bUz_eZtgesn-XnyzowL6Iy-6YOsQ',
authDomain: `${appName}.firebaseapp.com`,
databaseURL: `https://${appName}.firebaseio.com`,
projectId: appName,
storageBucket: '',
- messagingSenderId: '1094825197832'
+ messagingSenderId: '203115896556'
}
firebase.initializeApp(config)
diff --git a/admin/src/ducks/auth.js b/admin/src/ducks/auth.js
index 55ae8d0..ad390b5 100644
--- a/admin/src/ducks/auth.js
+++ b/admin/src/ducks/auth.js
@@ -61,16 +61,6 @@ export function signIn(email, password) {
payload: { email, password }
}
}
-/*
-export function signIn(email, password) {
- return (dispatch) => {
- firebase
- .auth()
- .signInWithEmailAndPassword(email, password)
- .then((user) => dispatch({ type: SIGN_IN_SUCCESS, payload: { user } }))
- }
-}
-*/
/**
* Sagas
diff --git a/admin/src/ducks/auth.test.js b/admin/src/ducks/auth.test.js
new file mode 100644
index 0000000..53dd2a5
--- /dev/null
+++ b/admin/src/ducks/auth.test.js
@@ -0,0 +1,126 @@
+import {
+ signUpSaga,
+ SIGN_UP_REQUEST,
+ SIGN_UP_SUCCESS,
+ SIGN_UP_ERROR,
+ signInSaga,
+ SIGN_IN_REQUEST,
+ SIGN_IN_REQUESTS_LIMIT,
+ SIGN_IN_SUCCESS,
+ SIGN_IN_ERROR
+} from './auth'
+import { call, put, take, apply } from 'redux-saga/effects'
+import firebase from 'firebase/app'
+
+describe('auth duck', () => {
+ it('should sign up', () => {
+ const user = {
+ email: 'test@test.com',
+ password: 'something_secret'
+ }
+
+ const saga = signUpSaga({
+ type: SIGN_UP_REQUEST,
+ payload: user
+ })
+
+ const auth = firebase.auth()
+
+ expect(saga.next().value).toEqual(
+ call(
+ [auth, auth.createUserWithEmailAndPassword],
+ user.email,
+ user.password
+ )
+ )
+
+ expect(saga.next(user).value).toEqual(
+ put({ type: SIGN_UP_SUCCESS, payload: { user } })
+ )
+ })
+
+ it('should handle failed sign up', () => {
+ const user = {
+ email: '',
+ password: ''
+ }
+
+ const saga = signUpSaga({
+ type: SIGN_UP_REQUEST,
+ payload: user
+ })
+
+ const auth = firebase.auth()
+
+ expect(saga.next().value).toEqual(
+ call(
+ [auth, auth.createUserWithEmailAndPassword],
+ user.email,
+ user.password
+ )
+ )
+
+ expect(saga.throw('error').value).toEqual(
+ put({ type: SIGN_UP_ERROR, error: 'error' })
+ )
+ })
+
+ it('should sign in', () => {
+ const user = {
+ email: 'test@test.com',
+ password: 'something_secret'
+ }
+
+ const saga = signInSaga()
+
+ expect(saga.next().value).toEqual(take(SIGN_IN_REQUEST))
+
+ const auth = firebase.auth()
+
+ expect(
+ saga.next({
+ type: SIGN_IN_REQUEST,
+ payload: user
+ }).value
+ ).toEqual(
+ apply(auth, auth.signInWithEmailAndPassword, [user.email, user.password])
+ )
+
+ expect(saga.next(user).value).toEqual(
+ put({ type: SIGN_IN_SUCCESS, payload: { user } })
+ )
+ })
+
+ it('should handle failed sign in', () => {
+ const user = {
+ email: '',
+ password: ''
+ }
+
+ const saga = signInSaga()
+
+ for (let i = 0; i < 3; i++) {
+ expect(saga.next().value).toEqual(take(SIGN_IN_REQUEST))
+
+ const auth = firebase.auth()
+
+ expect(
+ saga.next({
+ type: SIGN_IN_REQUEST,
+ payload: user
+ }).value
+ ).toEqual(
+ apply(auth, auth.signInWithEmailAndPassword, [
+ user.email,
+ user.password
+ ])
+ )
+
+ expect(saga.throw('error').value).toEqual(
+ put({ type: SIGN_IN_ERROR, error: 'error' })
+ )
+ }
+
+ expect(saga.next().value).toEqual(put({ type: SIGN_IN_REQUESTS_LIMIT }))
+ })
+})
diff --git a/admin/src/ducks/events.js b/admin/src/ducks/events.js
new file mode 100644
index 0000000..5a1138f
--- /dev/null
+++ b/admin/src/ducks/events.js
@@ -0,0 +1,94 @@
+import { appName } from '../config'
+import { Record, Map } from 'immutable'
+import { createSelector } from 'reselect'
+import { takeEvery, all, put, call } from 'redux-saga/effects'
+import firebase from 'firebase/app'
+
+/**
+ * Constants
+ * */
+export const moduleName = 'events'
+const prefix = `${appName}/${moduleName}`
+export const FETCH_EVENTS_REQUEST = `${prefix}/FETCH_EVENTS_REQUEST`
+export const FETCH_EVENTS_SUCCESS = `${prefix}/FETCH_EVENTS_SUCCESS`
+export const FETCH_EVENTS_FAIL = `${prefix}/FETCH_EVENTS_FAIL`
+
+/**
+ * Reducer
+ * */
+const ReducerState = Record({
+ loading: false,
+ loaded: false,
+ entities: new Map({})
+})
+
+const EventRecord = Record({
+ title: null,
+ url: null,
+ where: null,
+ when: null,
+ month: null,
+ submissionDeadline: null
+})
+
+export default function reducer(state = new ReducerState(), action) {
+ const { type, payload } = action
+
+ switch (type) {
+ case FETCH_EVENTS_REQUEST:
+ return state.set('loading', true)
+
+ case FETCH_EVENTS_SUCCESS:
+ return state
+ .set('loading', false)
+ .set('loaded', true)
+ .update('entities', (entities) =>
+ Object.entries(payload).reduce(
+ (map, [key, value]) => map.set(key, new EventRecord(value)),
+ new Map({})
+ )
+ )
+
+ case FETCH_EVENTS_FAIL:
+ return state.set('loading', false).set('loaded', false)
+
+ default:
+ return state
+ }
+}
+/**
+ * Selectors
+ * */
+
+export const stateSelector = (state) => state[moduleName]
+export const eventsSelector = createSelector(stateSelector, (state) =>
+ state.entities.toObject()
+)
+
+/**
+ * Action Creators
+ * */
+
+export function fetchEvents() {
+ return { type: FETCH_EVENTS_REQUEST }
+}
+
+/**
+ * Sagas
+ * */
+
+export function* fetchEventsSaga() {
+ const database = firebase.database().ref('/events')
+
+ try {
+ const data = yield call([database, database.once], 'value')
+
+ yield put({ type: FETCH_EVENTS_SUCCESS, payload: data.val() })
+ } catch (error) {
+ yield put({ type: FETCH_EVENTS_FAIL, payload: error })
+ }
+}
+
+export function* saga() {
+ yield all([takeEvery(FETCH_EVENTS_REQUEST, fetchEventsSaga)])
+}
diff --git a/admin/src/redux/reducer.js b/admin/src/redux/reducer.js
index f9c1879..7e88eca 100644
--- a/admin/src/redux/reducer.js
+++ b/admin/src/redux/reducer.js
@@ -3,10 +3,12 @@ import { routerReducer as router } from 'react-router-redux'
import { reducer as form } from 'redux-form'
import authReducer, { moduleName as authModule } from '../ducks/auth'
import peopleReducer, { moduleName as peopleModule } from '../ducks/people'
+import eventsReducer, { moduleName as eventsModule } from '../ducks/events'
export default combineReducers({
router,
form,
[authModule]: authReducer,
- [peopleModule]: peopleReducer
+ [peopleModule]: peopleReducer,
+ [eventsModule]: eventsReducer
})
diff --git a/admin/src/redux/saga.js b/admin/src/redux/saga.js
index 8f1e0cd..9d0ed64 100644
--- a/admin/src/redux/saga.js
+++ b/admin/src/redux/saga.js
@@ -1,7 +1,8 @@
import { all } from 'redux-saga/effects'
import { saga as authSaga } from '../ducks/auth'
import { saga as peopleSaga } from '../ducks/people'
+import { saga as eventsSaga } from '../ducks/events'
export default function*() {
- yield all([authSaga(), peopleSaga()])
+ yield all([authSaga(), peopleSaga(), eventsSaga()])
}
diff --git a/admin/src/routes/admin.js b/admin/src/routes/admin.js
index aa30167..f0edbd1 100644
--- a/admin/src/routes/admin.js
+++ b/admin/src/routes/admin.js
@@ -1,6 +1,7 @@
import React, { Component } from 'react'
import { Route } from 'react-router-dom'
import PersonPage from './person-page'
+import EventsPage from './events-page'
class AdminPage extends Component {
static propTypes = {}
@@ -10,6 +11,7 @@ class AdminPage extends Component {
Admin Page
+
)
}
diff --git a/admin/src/routes/events-page.js b/admin/src/routes/events-page.js
new file mode 100644
index 0000000..c898a97
--- /dev/null
+++ b/admin/src/routes/events-page.js
@@ -0,0 +1,35 @@
+import React, { Component } from 'react'
+import { connect } from 'react-redux'
+import EventsList from '../components/events/events-list'
+import { fetchEvents, stateSelector } from '../ducks/events'
+
+class EventsPage extends Component {
+ static propTypes = {}
+
+ componentWillMount = () => {
+ if (this.props.loading || this.props.loaded) return
+
+ this.props.fetchEvents()
+ }
+
+ render() {
+ const { loading, loaded } = this.props
+
+ const body = loading && !loaded ? Loading... :
+
+ return (
+
+
Events
+ {body}
+
+ )
+ }
+}
+
+export default connect(
+ (state) => {
+ const { loading, loaded } = stateSelector(state)
+ return { loading, loaded }
+ },
+ { fetchEvents }
+)(EventsPage)