diff --git a/admin/src/components/events/events-list.js b/admin/src/components/events/events-list.js
new file mode 100644
index 0000000..00a8880
--- /dev/null
+++ b/admin/src/components/events/events-list.js
@@ -0,0 +1,49 @@
+import React, { Component, Fragment } from 'react'
+import { getEvents, eventsSelector, moduleName } from '../../ducks/events'
+import { connect } from 'react-redux'
+import './events.css'
+
+class EventsList extends Component {
+ componentDidMount() {
+ this.props.getEvents()
+ }
+
+ render() {
+ const { events, fetching } = this.props
+ {
+ if (fetching) {
+ return
Loading...
+ } else {
+ return (
+
+ {events.map((event) => (
+
+
{event.title}
+
{event.when}
+
{event.where}
+
{event.url}
+
+ ))}
+
+ )
+ }
+ }
+ return (
+
+ {fetching ? (
+
(
+ ) : (
+
{events.map((event) => event.title)}
+ )}
+
+ )
+ }
+}
+
+export default connect(
+ (state) => ({
+ events: eventsSelector(state),
+ fetching: state[moduleName].fetching
+ }),
+ { getEvents }
+)(EventsList)
diff --git a/admin/src/components/events/events.css b/admin/src/components/events/events.css
new file mode 100644
index 0000000..fc58cbf
--- /dev/null
+++ b/admin/src/components/events/events.css
@@ -0,0 +1,16 @@
+.events-list-item {
+ box-shadow: 0 1px 3px 0 rgba(0,0,0,.2), 0 1px 1px 0 rgba(0,0,0,.14), 0 2px 1px -1px rgba(0,0,0,.12);
+ padding: 24px 16px;
+ font-family: Arial sans-serif;
+ margin: 8px;
+ max-width: 50%;
+}
+
+.events-list-item__title {
+ font-size: 24px;
+}
+
+.events-list-item__date {
+ font-size: 12px;
+ margin-bottom: 12px;
+}
\ No newline at end of file
diff --git a/admin/src/config.js b/admin/src/config.js
index a6889dc..413682c 100644
--- a/admin/src/config.js
+++ b/admin/src/config.js
@@ -5,12 +5,12 @@ import 'firebase/database'
export const appName = 'adv-react-25-06'
const config = {
- apiKey: 'AIzaSyDzqwnZ_39QyqhxYZVPjVH8eBww7DUBmVc',
- authDomain: `${appName}.firebaseapp.com`,
- databaseURL: `https://${appName}.firebaseio.com`,
- projectId: appName,
+ apiKey: 'AIzaSyD25TAZjq0-smHue8ViLsxv3nWBHm3Pjug',
+ authDomain: 'advreact-bed03.firebaseapp.com',
+ databaseURL: 'https://advreact-bed03.firebaseio.com',
+ projectId: 'advreact-bed03',
storageBucket: '',
- messagingSenderId: '874599443389'
+ messagingSenderId: '389238619888'
}
initializeApp(config)
diff --git a/admin/src/ducks/auth.spec.js b/admin/src/ducks/auth.spec.js
new file mode 100644
index 0000000..0d06c8a
--- /dev/null
+++ b/admin/src/ducks/auth.spec.js
@@ -0,0 +1,81 @@
+import { call, put, apply } from 'redux-saga/effects'
+import firebase from 'firebase/app'
+import {
+ SIGN_UP_REQUEST,
+ SIGN_UP_SUCCESS,
+ SIGN_IN_REQUEST,
+ SIGN_IN_SUCCESS,
+ signUpSaga,
+ signInSaga
+} from './auth'
+
+describe('Auth duck sign up saga', () => {
+ it('should sign up with new email and password', () => {
+ const auth = firebase.auth()
+ const credentials = {
+ email: `test${+new Date()}@test.com`,
+ password: 'qwerty123'
+ }
+
+ const action = {
+ type: SIGN_UP_REQUEST,
+ payload: credentials
+ }
+
+ const saga = signUpSaga(action)
+
+ expect(saga.next().value).toEqual(
+ call(
+ [auth, auth.createUserWithEmailAndPassword],
+ credentials.email,
+ credentials.password
+ )
+ )
+
+ const user = {
+ email: credentials.email,
+ uid: '123'
+ }
+
+ expect(saga.next(user).value).toEqual(
+ put({
+ type: SIGN_UP_SUCCESS,
+ payload: { user }
+ })
+ )
+ })
+
+ it('should sign in', () => {
+ const auth = firebase.auth()
+
+ const credentials = {
+ email: `test${+new Date()}@test.com`,
+ password: 'qwerty123'
+ }
+
+ const action = {
+ type: SIGN_IN_REQUEST,
+ payload: credentials
+ }
+
+ const saga = signInSaga(action)
+
+ expect(saga.next().value).toEqual(
+ apply(auth, auth.signInWithEmailAndPassword, [
+ credentials.email,
+ credentials.password
+ ])
+ )
+
+ const user = {
+ email: credentials.email
+ }
+
+ expect(saga.next(user).value).toEqual(
+ put({
+ type: SIGN_IN_SUCCESS,
+ payload: { user }
+ })
+ )
+ })
+})
diff --git a/admin/src/ducks/events.js b/admin/src/ducks/events.js
new file mode 100644
index 0000000..dfb0e59
--- /dev/null
+++ b/admin/src/ducks/events.js
@@ -0,0 +1,80 @@
+import { Record, OrderedMap } from 'immutable'
+import { put, takeLatest, call } from 'redux-saga/effects'
+import { createSelector } from 'reselect'
+import { delay } from 'redux-saga'
+import firebase from 'firebase/app'
+
+// Constants
+export const moduleName = 'events'
+export const GET_EVENTS_REQUEST = `${moduleName}/GET_EVENTS_REQUEST`
+export const GET_EVENTS_SUCCESS = `${moduleName}/GET_EVENTS_SUCCESS`
+export const GET_EVENTS_ERROR = `${moduleName}/GET_EVENTS_ERROR`
+
+// Reducer
+const ReducerState = Record({
+ entities: new OrderedMap(),
+ fetching: false
+})
+
+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 GET_EVENTS_REQUEST:
+ return state.set('fetching', true)
+ case GET_EVENTS_SUCCESS:
+ return state
+ .set('fetching', false)
+ .set('entities', eventsResponseToState(payload))
+ case GET_EVENTS_ERROR:
+ return state.set('fetching', false)
+ default:
+ return state
+ }
+}
+
+// Selectors
+export const stateSelector = (state) => state[moduleName]
+export const eventsSelector = createSelector(stateSelector, (state) =>
+ state.entities.valueSeq().toArray()
+)
+
+// Action Creators
+export const getEvents = () => ({
+ type: GET_EVENTS_REQUEST
+})
+
+// Sagas
+function* getEventsSaga() {
+ const ref = firebase.database().ref('/events')
+ // делей для видимости лоадера
+ yield call(delay, 1000)
+
+ const snapshot = yield call([ref, ref.once], 'value')
+
+ yield put({
+ type: GET_EVENTS_SUCCESS,
+ payload: snapshot.val()
+ })
+}
+
+export function* saga() {
+ yield takeLatest(GET_EVENTS_REQUEST, getEventsSaga)
+}
+
+// utils
+export const eventsResponseToState = (values) => {
+ return Object.entries(values).reduce(
+ (item, [uid, value]) => item.set(uid, new EventRecord({ uid, ...value })),
+ new OrderedMap({})
+ )
+}
diff --git a/admin/src/redux/reducer.js b/admin/src/redux/reducer.js
index 60ea924..c28e1e8 100644
--- a/admin/src/redux/reducer.js
+++ b/admin/src/redux/reducer.js
@@ -2,9 +2,11 @@ import { combineReducers } from '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({
form,
[authModule]: authReducer,
- [peopleModule]: peopleReducer
+ [peopleModule]: peopleReducer,
+ [eventsModule]: eventsReducer
})
diff --git a/admin/src/redux/saga.js b/admin/src/redux/saga.js
index ca88652..71a1ad8 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 peopleSaga } from '../ducks/people'
import { saga as authSaga } from '../ducks/auth'
+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..4d5ccbb
--- /dev/null
+++ b/admin/src/routes/events-page.js
@@ -0,0 +1,15 @@
+import React, { Component } from 'react'
+import EventsList from '../components/events/events-list'
+
+class EventsPage extends Component {
+ render() {
+ return (
+
+
Events list
+
+
+ )
+ }
+}
+
+export default EventsPage