├── .eslintrc
├── public
├── favicon.ico
├── assets
│ ├── logo.png
│ ├── user.png
│ ├── ralph.jpg
│ └── categoryImages
│ │ ├── film.jpg
│ │ ├── math.jpg
│ │ ├── physics.jpg
│ │ ├── chemistry.jpg
│ │ ├── engineering.jpg
│ │ └── literature.jpg
├── favicon-goober.ico
├── favicon-react.ico
├── manifest.json
└── index.html
├── docs
├── source
│ ├── testwebapp-dev-todo.rst
│ ├── help.rst
│ ├── index.rst
│ ├── testwebapp-quickstart.rst
│ ├── testwebapp-fork-acceptance.rst
│ ├── testwebapp-fork-todo.rst
│ ├── help-build-docs.rst
│ ├── testwebapp-fork-ralph.rst
│ └── conf.py
├── Makefile
└── make.bat
├── src
├── features
│ ├── auth
│ │ ├── authConstants.jsx
│ │ ├── SocialLogin
│ │ │ └── SocialLogin.jsx
│ │ ├── authReducer.jsx
│ │ ├── Login
│ │ │ └── LoginForm.jsx
│ │ ├── Register
│ │ │ └── RegisterForm.jsx
│ │ └── authActions.jsx
│ ├── modals
│ │ ├── modalConstants.jsx
│ │ ├── modalActions.jsx
│ │ ├── modalReducer.jsx
│ │ ├── TestModal.jsx
│ │ ├── ModalManager.jsx
│ │ ├── LoginModal.jsx
│ │ └── RegisterModal.jsx
│ ├── async
│ │ ├── asyncConstants.jsx
│ │ ├── asyncActions.jsx
│ │ └── asyncReducer.jsx
│ ├── request
│ │ ├── requestConstants.jsx
│ │ ├── RequestDetail
│ │ │ ├── RequestDetailChat.jsx
│ │ │ └── RequestDetailPage.jsx
│ │ ├── requestReducer.jsx
│ │ ├── RequestForm
│ │ │ └── RequestForm.jsx
│ │ └── requestActions.jsx
│ ├── session
│ │ ├── sessionConstants.jsx
│ │ ├── SessionActivity
│ │ │ └── SessionActivity.jsx
│ │ ├── SessionList
│ │ │ ├── SessionListAttendee.jsx
│ │ │ ├── SessionList.jsx
│ │ │ └── SessionListItem.jsx
│ │ ├── RalphDetail
│ │ │ ├── SessionDetailMap.jsx
│ │ │ ├── SessionDetailChatForm.jsx
│ │ │ ├── SessionDetailSidebar.jsx
│ │ │ ├── SessionDetailHeader.jsx
│ │ │ ├── SessionDetailInfo.jsx
│ │ │ ├── SessionDetailPage.jsx
│ │ │ └── SessionDetailChat.jsx
│ │ ├── SessionDetail
│ │ │ ├── SessionDetailMap.jsx
│ │ │ ├── SessionDetailChatForm.jsx
│ │ │ ├── SessionDetailSidebar.jsx
│ │ │ ├── SessionDetailHeader.jsx
│ │ │ ├── SessionDetailInfo.jsx
│ │ │ ├── SessionDetailPage.jsx
│ │ │ └── SessionDetailChat.jsx
│ │ ├── sessionReducer.jsx
│ │ ├── SessionDashboard
│ │ │ └── SessionDashboard.jsx
│ │ ├── sessionActions.jsx
│ │ └── RalphForm
│ │ │ └── SessionForm.jsx
│ ├── subject
│ │ ├── subjectConstants.jsx
│ │ ├── SubjectDetail
│ │ │ └── SubjectDetailPage.jsx
│ │ ├── SubjectList
│ │ │ ├── SubjectListProvider.jsx
│ │ │ ├── SubjectList.jsx
│ │ │ └── SubjectListItem.jsx
│ │ ├── subjectReducer.jsx
│ │ ├── subjectActions.jsx
│ │ ├── SubjectForm
│ │ │ └── SubjectForm.jsx
│ │ └── SubjectDashboard
│ │ │ └── SubjectDashboard.jsx
│ ├── user
│ │ ├── PeopleDashboard
│ │ │ └── PeopleDashboard.jsx
│ │ ├── userQueries.jsx
│ │ ├── UserDetailed
│ │ │ ├── UserDetailedSidebar.jsx
│ │ │ ├── UserDetailedPhotos.jsx
│ │ │ ├── UserDetailedHeader.jsx
│ │ │ ├── UserDetailedDescription.jsx
│ │ │ ├── UserDetailedSessions.jsx
│ │ │ └── UserDetailedPage.jsx
│ │ └── Settings
│ │ │ ├── SettingsNav.jsx
│ │ │ ├── SettingsDashboard.jsx
│ │ │ ├── BasicPage.jsx
│ │ │ ├── AccountPage.jsx
│ │ │ ├── AboutPage.jsx
│ │ │ └── PhotosPage.jsx
│ ├── callRalph
│ │ ├── testConstants.jsx
│ │ ├── testActions.jsx
│ │ ├── testReducer.js
│ │ └── TestComponent.jsx
│ ├── testarea
│ │ ├── testConstants.jsx
│ │ ├── testActions.jsx
│ │ ├── testReducer.js
│ │ └── TestComponent.jsx
│ ├── nav
│ │ ├── Menus
│ │ │ ├── SignedOutMenu.jsx
│ │ │ └── SignedInMenu.jsx
│ │ └── NavBar
│ │ │ └── NavBar.jsx
│ └── home
│ │ └── HomePage.jsx
├── app
│ ├── layout
│ │ ├── NoSessions.jsx
│ │ ├── NotFound.jsx
│ │ ├── LoadingComponent.jsx
│ │ └── App.jsx
│ ├── common
│ │ ├── util
│ │ │ ├── reducerUtil.js
│ │ │ ├── ScrollToTop.jsx
│ │ │ └── helpers.js
│ │ └── form
│ │ │ ├── RadioInput.jsx
│ │ │ ├── TextInput.jsx
│ │ │ ├── TextArea.jsx
│ │ │ ├── SelectInput.jsx
│ │ │ ├── DateInput.jsx
│ │ │ └── PlaceInput.jsx
│ ├── data
│ │ ├── mockApi.js
│ │ └── sampleData.js
│ ├── config
│ │ └── firebase.js
│ ├── reducers
│ │ └── rootReducer.js
│ └── store
│ │ └── configureStore.js
├── index.js
├── index.css
└── registerServiceWorker.js
├── README.md
├── app-keys-env-example
├── app-keys.env
├── functions
├── index.js
├── package.json
└── .eslintrc.json
├── .vscode
└── launch.json
├── .gitignore
├── firebase.json
└── package.json
/.eslintrc:
--------------------------------------------------------------------------------
1 | {
2 | "extends": "react-app"
3 | }
--------------------------------------------------------------------------------
/public/favicon.ico:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/2Cld/testwebapp/master/public/favicon.ico
--------------------------------------------------------------------------------
/public/assets/logo.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/2Cld/testwebapp/master/public/assets/logo.png
--------------------------------------------------------------------------------
/public/assets/user.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/2Cld/testwebapp/master/public/assets/user.png
--------------------------------------------------------------------------------
/public/assets/ralph.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/2Cld/testwebapp/master/public/assets/ralph.jpg
--------------------------------------------------------------------------------
/public/favicon-goober.ico:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/2Cld/testwebapp/master/public/favicon-goober.ico
--------------------------------------------------------------------------------
/public/favicon-react.ico:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/2Cld/testwebapp/master/public/favicon-react.ico
--------------------------------------------------------------------------------
/docs/source/testwebapp-dev-todo.rst:
--------------------------------------------------------------------------------
1 | testwebapp dev todo
2 | ===================
3 |
4 | #. TODO: TBD
5 | #. TODO: TBD
--------------------------------------------------------------------------------
/public/assets/categoryImages/film.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/2Cld/testwebapp/master/public/assets/categoryImages/film.jpg
--------------------------------------------------------------------------------
/public/assets/categoryImages/math.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/2Cld/testwebapp/master/public/assets/categoryImages/math.jpg
--------------------------------------------------------------------------------
/src/features/auth/authConstants.jsx:
--------------------------------------------------------------------------------
1 | export const LOGIN_USER = "LOGIN_USER";
2 | export const SIGN_OUT_USER = "SIGN_OUT_USER";
--------------------------------------------------------------------------------
/src/features/modals/modalConstants.jsx:
--------------------------------------------------------------------------------
1 | export const MODAL_OPEN = "MODAL_OPEN";
2 | export const MODAL_CLOSE = "MODAL_CLOSE";
--------------------------------------------------------------------------------
/public/assets/categoryImages/physics.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/2Cld/testwebapp/master/public/assets/categoryImages/physics.jpg
--------------------------------------------------------------------------------
/public/assets/categoryImages/chemistry.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/2Cld/testwebapp/master/public/assets/categoryImages/chemistry.jpg
--------------------------------------------------------------------------------
/public/assets/categoryImages/engineering.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/2Cld/testwebapp/master/public/assets/categoryImages/engineering.jpg
--------------------------------------------------------------------------------
/public/assets/categoryImages/literature.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/2Cld/testwebapp/master/public/assets/categoryImages/literature.jpg
--------------------------------------------------------------------------------
/src/app/layout/NoSessions.jsx:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 |
3 | export default () => {
4 | return (
5 |
6 |
No Sessions Scheduled.
7 |
8 | )
9 | }
--------------------------------------------------------------------------------
/src/app/layout/NotFound.jsx:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 |
3 | export default () => {
4 | return (
5 |
6 |
Error 404 page not found
7 |
8 | )
9 | }
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # testwebapp
2 |
3 | Goober Testwebapp ReactJS application.
4 |
5 | ## Getting Started
6 |
7 | This project was bootstrapped with [Create React App](https://github.com/facebookincubator/create-react-app).
--------------------------------------------------------------------------------
/src/features/async/asyncConstants.jsx:
--------------------------------------------------------------------------------
1 | export const ASYNC_ACTION_START = "ASYNC_ACTION_START";
2 | export const ASYNC_ACTION_FINISH = "ASYNC_ACTION_FINISH";
3 | export const ASYNC_ACTION_ERROR = "ASYNC_ACTION_ERROR";
--------------------------------------------------------------------------------
/app-keys-env-example:
--------------------------------------------------------------------------------
1 | GOOBERU_API_KEY="yourAPIkey"
2 | GOOBERU_SENDERID="yourSENDERID"
3 | GOOBERU_MAPS_KEY="yourMAPSApiKey"
4 | GOOBERU_MAPS_URL="https://maps.googleapis.com/maps/api/js?key=YourMAPSApiKey&libraries=places"
5 |
6 |
7 |
8 |
--------------------------------------------------------------------------------
/src/features/request/requestConstants.jsx:
--------------------------------------------------------------------------------
1 | export const CREATE_REQUEST = "CREATE_REQUEST";
2 | export const UPDATE_REQUEST = "UPDATE_REQUEST";
3 | export const DELETE_REQUEST = "DELETE_REQUEST";
4 | export const FETCH_REQUESTS = "FETCH_REQUESTS";
--------------------------------------------------------------------------------
/src/features/session/sessionConstants.jsx:
--------------------------------------------------------------------------------
1 | export const CREATE_SESSION = "CREATE_SESSION";
2 | export const UPDATE_SESSION = "UPDATE_SESSION";
3 | export const DELETE_SESSION = "DELETE_SESSION";
4 | export const FETCH_SESSIONS = "FETCH_SESSIONS";
--------------------------------------------------------------------------------
/src/features/subject/subjectConstants.jsx:
--------------------------------------------------------------------------------
1 | export const CREATE_SUBJECT = "CREATE_SUBJECT";
2 | export const UPDATE_SUBJECT = "UPDATE_SUBJECT";
3 | export const DELETE_SUBJECT = "DELETE_SUBJECT";
4 | export const FETCH_SUBJECTS = "FETCH_SUBJECTS";
--------------------------------------------------------------------------------
/src/app/common/util/reducerUtil.js:
--------------------------------------------------------------------------------
1 | export const createReducer = (initialState, fnMap) => {
2 | return (state = initialState, { type, payload }) => {
3 | const handler = fnMap[type];
4 |
5 | return handler ? handler(state, payload) : state;
6 | };
7 | };
8 |
--------------------------------------------------------------------------------
/src/features/user/PeopleDashboard/PeopleDashboard.jsx:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 |
3 | const PeopleDashboard = () => {
4 | return (
5 |
6 |
PeopleDashboard
7 |
8 | )
9 | };
10 |
11 | export default PeopleDashboard;
12 |
--------------------------------------------------------------------------------
/src/features/callRalph/testConstants.jsx:
--------------------------------------------------------------------------------
1 | export const INCREMENT_COUNTER = "INCREMENT_COUNTER";
2 | export const DECREMENT_COUNTER = "DECREMENT_COUNTER";
3 | export const COUNTER_ACTION_START = "COUNTER_ACTION_START";
4 | export const COUNTER_ACTION_DONE = "COUNTER_ACTION_DONE";
--------------------------------------------------------------------------------
/src/features/testarea/testConstants.jsx:
--------------------------------------------------------------------------------
1 | export const INCREMENT_COUNTER = "INCREMENT_COUNTER";
2 | export const DECREMENT_COUNTER = "DECREMENT_COUNTER";
3 | export const COUNTER_ACTION_START = "COUNTER_ACTION_START";
4 | export const COUNTER_ACTION_DONE = "COUNTER_ACTION_DONE";
--------------------------------------------------------------------------------
/src/features/subject/SubjectDetail/SubjectDetailPage.jsx:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 |
3 | const SubjectDetailPage = () => {
4 | return (
5 |
6 |
SubjectDetailPage
7 |
8 | )
9 | };
10 |
11 | export default SubjectDetailPage;
12 |
--------------------------------------------------------------------------------
/app-keys.env:
--------------------------------------------------------------------------------
1 |
2 |
3 | GOOBERU_API_KEY="AIzaSyBxGiU3Cm0r-TYaoNzzrTWtZNVB4MzZzQI"
4 | GOOBERU_SENDERID="931119937625"
5 | GOOBERU_MAPS_KEY="AIzaSyDQpU2xxESgXFfCZZV5DgKOLdvFndaGbh8"
6 | GOOBERU_MAPS_URL="https://maps.googleapis.com/maps/api/js?key=AIzaSyDQpU2xxESgXFfCZZV5DgKOLdvFndaGbh8&libraries=places"
7 |
8 |
--------------------------------------------------------------------------------
/src/app/data/mockApi.js:
--------------------------------------------------------------------------------
1 | import sampleData from './sampleData';
2 |
3 | const delay = (ms) => {
4 | return new Promise(resolve => setTimeout(resolve, ms))
5 | };
6 |
7 | export const fetchSampleData = () => {
8 | return delay(1000).then(() => {
9 | return Promise.resolve(sampleData)
10 | })
11 | };
--------------------------------------------------------------------------------
/functions/index.js:
--------------------------------------------------------------------------------
1 | const functions = require('firebase-functions');
2 |
3 | // // Create and Deploy Your First Cloud Functions
4 | // // https://firebase.google.com/docs/functions/write-firebase-functions
5 | //
6 | // exports.helloWorld = functions.https.onRequest((request, response) => {
7 | // response.send("Hello from Firebase!");
8 | // });
9 |
--------------------------------------------------------------------------------
/src/app/layout/LoadingComponent.jsx:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 | import { Dimmer, Loader } from 'semantic-ui-react';
3 |
4 | const LoadingComponent = ({inverted}) => {
5 | return (
6 |
7 |
8 |
9 | )
10 | };
11 |
12 | export default LoadingComponent
--------------------------------------------------------------------------------
/public/manifest.json:
--------------------------------------------------------------------------------
1 | {
2 | "short_name": "GooberU",
3 | "name": "Goober App TestWebApp",
4 | "icons": [
5 | {
6 | "src": "favicon.ico",
7 | "sizes": "64x64 32x32 24x24 16x16",
8 | "type": "image/x-icon"
9 | }
10 | ],
11 | "start_url": "./index.html",
12 | "display": "standalone",
13 | "theme_color": "#000000",
14 | "background_color": "#ffffff"
15 | }
16 |
--------------------------------------------------------------------------------
/src/features/modals/modalActions.jsx:
--------------------------------------------------------------------------------
1 | import { MODAL_CLOSE, MODAL_OPEN } from './modalConstants';
2 |
3 | export const openModal = (modalType, modalProps) => {
4 | return {
5 | type: MODAL_OPEN,
6 | payload: {
7 | modalType,
8 | modalProps
9 | }
10 | }
11 | };
12 |
13 | export const closeModal = () => {
14 | return {
15 | type: MODAL_CLOSE
16 | }
17 | };
--------------------------------------------------------------------------------
/src/app/common/form/RadioInput.jsx:
--------------------------------------------------------------------------------
1 | import React from 'react'
2 | import { Form } from 'semantic-ui-react'
3 |
4 | const RadioInput = ({input, width, type, label}) => {
5 | return (
6 |
7 |
8 | {' '}
9 |
10 |
11 |
12 | )
13 | }
14 |
15 | export default RadioInput
16 |
--------------------------------------------------------------------------------
/src/features/session/SessionActivity/SessionActivity.jsx:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 | import { Header, Segment } from 'semantic-ui-react';
3 |
4 | const SessionActivity = () => {
5 | return (
6 |
7 |
8 |
9 | Recent activity
10 |
11 |
12 | )
13 | };
14 |
15 | export default SessionActivity
--------------------------------------------------------------------------------
/src/app/common/util/ScrollToTop.jsx:
--------------------------------------------------------------------------------
1 | import { Component } from "react";
2 | import { withRouter } from "react-router-dom";
3 |
4 | class ScrollToTop extends Component {
5 | componentDidUpdate(prevProps) {
6 | if (this.props.location !== prevProps.location) {
7 | window.scrollTo(0, 0);
8 | }
9 | }
10 |
11 | render() {
12 | return this.props.children;
13 | }
14 | }
15 |
16 | export default withRouter(ScrollToTop);
17 |
--------------------------------------------------------------------------------
/docs/source/help.rst:
--------------------------------------------------------------------------------
1 | Help
2 | ====
3 |
4 | Ping cat_at_bast23_dot_me
5 |
6 | Various help various tool chain setups.
7 |
8 | .. toctree::
9 | :maxdepth: 2
10 |
11 | help-build-docs
12 |
13 |
14 | Things
15 | ------
16 |
17 | idempotent - denoting an element of a set that is unchanged in value when multiplied or otherwise operated on by itself.
18 | immutable - unchanging over time or unable to be changed.
19 | ephemeral - lasting for a very short time.
--------------------------------------------------------------------------------
/docs/source/index.rst:
--------------------------------------------------------------------------------
1 | ==================
2 | gooberu-testwebapp
3 | ==================
4 |
5 | Guide
6 | ^^^^^
7 |
8 | .. toctree::
9 | :maxdepth: 2
10 |
11 | testwebapp-quickstart
12 | testwebapp-dev-todo
13 | testwebapp-dev-detail
14 | testwebapp-fork-todo
15 | testwebapp-fork-ralph
16 | testwebapp-fork-acceptance
17 | help
18 |
19 | test
20 |
21 | Indices and tables
22 | ==================
23 |
24 | * :ref:`genindex`
25 | * :ref:`search`
--------------------------------------------------------------------------------
/src/features/subject/SubjectList/SubjectListProvider.jsx:
--------------------------------------------------------------------------------
1 | import React, { Component } from 'react';
2 | import { List, Image } from 'semantic-ui-react';
3 |
4 | class SubjectListProvider extends Component {
5 | render() {
6 | const {attendee} = this.props;
7 | return (
8 |
9 |
10 |
11 | )
12 | }
13 | };
14 |
15 | export default SubjectListProvider;
16 |
--------------------------------------------------------------------------------
/src/features/auth/SocialLogin/SocialLogin.jsx:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 | import { Button, Icon } from 'semantic-ui-react';
3 |
4 | const SocialLogin = ({socialLogin}) => {
5 | return (
6 |
7 |
11 |
12 | );
13 | };
14 |
15 | export default SocialLogin;
16 |
--------------------------------------------------------------------------------
/src/features/async/asyncActions.jsx:
--------------------------------------------------------------------------------
1 | import { ASYNC_ACTION_ERROR, ASYNC_ACTION_FINISH, ASYNC_ACTION_START } from './asyncConstants';
2 |
3 | export const asyncActionStart = () => {
4 | return {
5 | type: ASYNC_ACTION_START
6 | }
7 | };
8 |
9 | export const asyncActionFinish = () => {
10 | return {
11 | type: ASYNC_ACTION_FINISH
12 | }
13 | };
14 |
15 | export const asyncActionError = () => {
16 | return {
17 | type: ASYNC_ACTION_ERROR
18 | }
19 | };
--------------------------------------------------------------------------------
/src/app/common/form/TextInput.jsx:
--------------------------------------------------------------------------------
1 | import React from 'react'
2 | import { Form, Label } from 'semantic-ui-react'
3 |
4 | const TextInput = ({input, width, type, placeholder, meta: {touched, error}}) => {
5 | return (
6 |
7 |
8 | {touched && error && }
9 |
10 | )
11 | }
12 |
13 | export default TextInput
14 |
--------------------------------------------------------------------------------
/src/app/common/form/TextArea.jsx:
--------------------------------------------------------------------------------
1 | import React from 'react'
2 | import { Form, Label } from 'semantic-ui-react'
3 |
4 | const TextArea = ({input, rows, width, type, placeholder, meta: {touched, error}}) => {
5 | return (
6 |
7 |
8 | {touched && error && }
9 |
10 | )
11 | }
12 |
13 | export default TextArea
14 |
--------------------------------------------------------------------------------
/src/features/nav/Menus/SignedOutMenu.jsx:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 | import { Menu, Button } from 'semantic-ui-react';
3 |
4 | const SignedOutMenu = ({signIn, register}) => {
5 | return (
6 |
7 |
8 |
15 |
16 | )
17 | };
18 |
19 | export default SignedOutMenu
--------------------------------------------------------------------------------
/src/features/session/SessionList/SessionListAttendee.jsx:
--------------------------------------------------------------------------------
1 | import React, { Component } from 'react';
2 | import { List, Image } from 'semantic-ui-react';
3 | import { Link } from 'react-router-dom';
4 |
5 | class SessionListAttendee extends Component {
6 | render() {
7 | const {attendee} = this.props;
8 | return (
9 |
10 |
11 |
12 | )
13 | }
14 | };
15 |
16 | export default SessionListAttendee
17 |
--------------------------------------------------------------------------------
/.vscode/launch.json:
--------------------------------------------------------------------------------
1 | {
2 | // Use IntelliSense to learn about possible attributes.
3 | // Hover to view descriptions of existing attributes.
4 | // For more information, visit: https://go.microsoft.com/fwlink/?linkid=830387
5 | "version": "0.2.0",
6 | "configurations": [
7 |
8 | {
9 | "type": "chrome",
10 | "request": "launch",
11 | "name": "Launch Chrome against localhost",
12 | "url": "http://localhost:3000",
13 | "webRoot": "${workspaceFolder}/src"
14 | }
15 | ]
16 | }
--------------------------------------------------------------------------------
/src/features/modals/modalReducer.jsx:
--------------------------------------------------------------------------------
1 | import { MODAL_CLOSE, MODAL_OPEN } from './modalConstants';
2 | import { createReducer } from '../../app/common/util/reducerUtil';
3 |
4 | const initialState = null;
5 |
6 | export const openModal = (state, payload) => {
7 | const {modalType, modalProps} = payload;
8 | return {modalType, modalProps}
9 | };
10 |
11 | export const closeModal = (state, payload) => {
12 | return null
13 | };
14 |
15 | export default createReducer(initialState, {
16 | [MODAL_OPEN]: openModal,
17 | [MODAL_CLOSE]: closeModal
18 | });
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | # See https://help.github.com/ignore-files/ for more about ignoring files.
2 |
3 | # dependencies
4 | /node_modules
5 |
6 | # testing
7 | /coverage
8 |
9 | # production
10 | /build
11 |
12 | # misc
13 | .DS_Store
14 | .env.local
15 | .env.development.local
16 | .env.test.local
17 | .env.production.local
18 |
19 | npm-debug.log*
20 | yarn-debug.log*
21 | yarn-error.log*
22 |
23 | functions/node_modules/**
24 |
25 | #######################
26 | # readthedocs gitignore
27 | # sphinx build folder
28 | docs/build/
29 |
30 | #######################
31 | # API Key gitignore
32 | app-keys.js
--------------------------------------------------------------------------------
/firebase.json:
--------------------------------------------------------------------------------
1 | {
2 | "functions": {
3 | "predeploy": [
4 | "npm --prefix \"$RESOURCE_DIR\" run lint"
5 | ]
6 | },
7 | "hosting": {
8 | "public": "build",
9 | "ignore": [
10 | "firebase.json",
11 | "**/.*",
12 | "**/node_modules/**"
13 | ],
14 | "rewrites": [
15 | {
16 | "source": "**",
17 | "destination": "/index.html"
18 | }
19 | ],
20 | "headers": [{
21 | "source": "/service-workers.js",
22 | "headers": [{
23 | "key": "Cache-Control",
24 | "value": "max-age=0"
25 | }]
26 | }]
27 | }
28 | }
29 |
--------------------------------------------------------------------------------
/functions/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "functions",
3 | "description": "Cloud Functions for Firebase",
4 | "scripts": {
5 | "lint": "eslint .",
6 | "serve": "firebase serve --only functions",
7 | "shell": "firebase functions:shell",
8 | "start": "npm run shell",
9 | "deploy": "firebase deploy --only functions",
10 | "logs": "firebase functions:log"
11 | },
12 | "dependencies": {
13 | "firebase-admin": "~6.0.0",
14 | "firebase-functions": "^2.0.3"
15 | },
16 | "devDependencies": {
17 | "eslint": "^4.12.0",
18 | "eslint-plugin-promise": "^3.6.0"
19 | },
20 | "private": true
21 | }
22 |
--------------------------------------------------------------------------------
/src/app/common/form/SelectInput.jsx:
--------------------------------------------------------------------------------
1 | import React from 'react'
2 | import { Form, Label, Select } from 'semantic-ui-react'
3 |
4 | const SelectInput = ({input, type, placeholder, multiple, options, meta: {touched, error}}) => {
5 | return (
6 |
7 |
16 | )
17 | }
18 |
19 | export default SelectInput
20 |
--------------------------------------------------------------------------------
/src/app/config/firebase.js:
--------------------------------------------------------------------------------
1 | import firebase from 'firebase';
2 | import 'firebase/firestore';
3 |
4 | const firebaseConfig = {
5 | apiKey: "AIzaSyBxGiU3Cm0r-TYaoNzzrTWtZNVB4MzZzQI",
6 | authDomain: "gooberu-testscreens.firebaseapp.com",
7 | databaseURL: "https://gooberu-testscreens.firebaseio.com",
8 | projectId: "gooberu-testscreens",
9 | storageBucket: "gooberu-testscreens.appspot.com",
10 | messagingSenderId: "931119937625"
11 | }
12 |
13 | firebase.initializeApp(firebaseConfig);
14 | const firestore = firebase.firestore();
15 | const settings = {
16 | timestampsInSnapshots: true
17 | }
18 | firestore.settings(settings);
19 |
20 | export default firebase;
21 |
--------------------------------------------------------------------------------
/docs/Makefile:
--------------------------------------------------------------------------------
1 | # Minimal makefile for Sphinx documentation
2 | #
3 |
4 | # You can set these variables from the command line.
5 | SPHINXOPTS =
6 | SPHINXBUILD = sphinx-build
7 | SPHINXPROJ = rtddemo
8 | SOURCEDIR = source
9 | BUILDDIR = build
10 |
11 | # Put it first so that "make" without argument is like "make help".
12 | help:
13 | @$(SPHINXBUILD) -M help "$(SOURCEDIR)" "$(BUILDDIR)" $(SPHINXOPTS) $(O)
14 |
15 | .PHONY: help Makefile
16 |
17 | # Catch-all target: route all unknown targets to Sphinx using the new
18 | # "make mode" option. $(O) is meant as a shortcut for $(SPHINXOPTS).
19 | %: Makefile
20 | @$(SPHINXBUILD) -M $@ "$(SOURCEDIR)" "$(BUILDDIR)" $(SPHINXOPTS) $(O)
--------------------------------------------------------------------------------
/src/features/auth/authReducer.jsx:
--------------------------------------------------------------------------------
1 | import { LOGIN_USER, SIGN_OUT_USER } from './authConstants';
2 | import { createReducer } from '../../app/common/util/reducerUtil';
3 |
4 | const initialState = {
5 | currentUser: {}
6 | };
7 |
8 | export const loginUser = (state, payload) => {
9 | return {
10 | ...state,
11 | authenticated: true,
12 | currentUser: payload.creds.email
13 | }
14 | };
15 |
16 | export const signOutUser = (state, payload) => {
17 | return {
18 | ...state,
19 | authenticated: false,
20 | currentUser: {}
21 | }
22 | };
23 |
24 | export default createReducer(initialState, {
25 | [LOGIN_USER]: loginUser,
26 | [SIGN_OUT_USER]: signOutUser
27 | });
--------------------------------------------------------------------------------
/src/features/modals/TestModal.jsx:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 | import { Modal } from 'semantic-ui-react';
3 | import { closeModal } from './modalActions';
4 | import { connect } from 'react-redux';
5 |
6 | const actions = {
7 | closeModal
8 | };
9 |
10 | const TestModal = ({closeModal}) => {
11 | return (
12 |
13 | Test Modal
14 |
15 |
16 | Test Modal... nothing to see here
17 |
18 |
19 |
20 | );
21 | };
22 |
23 | export default connect(null, actions)(TestModal);
24 |
--------------------------------------------------------------------------------
/src/features/user/userQueries.jsx:
--------------------------------------------------------------------------------
1 | export const userDetailedQuery = ({ auth, userUid }) => {
2 | if (userUid !== null) {
3 | return [
4 | {
5 | collection: 'users',
6 | doc: userUid,
7 | storeAs: 'profile'
8 | },
9 | {
10 | collection: 'users',
11 | doc: userUid,
12 | subcollections: [{ collection: 'photos' }],
13 | storeAs: 'photos'
14 | }
15 | ];
16 | } else {
17 | return [
18 | {
19 | collection: 'users',
20 | doc: auth.uid,
21 | subcollections: [{ collection: 'photos' }],
22 | storeAs: 'photos'
23 | }
24 | ];
25 | }
26 | };
27 |
--------------------------------------------------------------------------------
/src/features/user/UserDetailed/UserDetailedSidebar.jsx:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 | import { Button, Grid, Segment } from 'semantic-ui-react';
3 | import { Link } from 'react-router-dom';
4 |
5 | const UserDetailedSidebar = ({ isCurrentUser }) => {
6 | return (
7 |
8 |
9 | {isCurrentUser ? (
10 |
18 | ) : (
19 |
20 | )}
21 |
22 |
23 | );
24 | };
25 |
26 | export default UserDetailedSidebar;
27 |
--------------------------------------------------------------------------------
/src/features/async/asyncReducer.jsx:
--------------------------------------------------------------------------------
1 | import { createReducer } from '../../app/common/util/reducerUtil';
2 | import { ASYNC_ACTION_START, ASYNC_ACTION_FINISH, ASYNC_ACTION_ERROR } from './asyncConstants';
3 |
4 | const initialState = {
5 | loading: false
6 | };
7 |
8 | export const asyncActionStarted = (state, payload) => {
9 | return {...state, loading: true}
10 | };
11 |
12 | export const asyncActionFinished = (state) => {
13 | return {...state, loading: false}
14 | };
15 |
16 | export const asyncActionError = (state) => {
17 | return {...state, loading: false}
18 | };
19 |
20 | export default createReducer(initialState, {
21 | [ASYNC_ACTION_START]: asyncActionStarted,
22 | [ASYNC_ACTION_FINISH]: asyncActionFinished,
23 | [ASYNC_ACTION_ERROR]: asyncActionError
24 | });
--------------------------------------------------------------------------------
/src/features/modals/ModalManager.jsx:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 | import { connect } from 'react-redux';
3 | import TestModal from './TestModal';
4 | import LoginModal from './LoginModal';
5 | import RegisterModal from './RegisterModal';
6 |
7 | const modalLookup = {
8 | TestModal,
9 | LoginModal,
10 | RegisterModal
11 | };
12 |
13 | const mapState = (state) => ({
14 | currentModal: state.modals
15 | });
16 |
17 | const ModalManager = ({currentModal}) => {
18 | let renderedModal;
19 | if (currentModal) {
20 | const {modalType, modalProps} = currentModal;
21 | const ModalComponent = modalLookup[modalType];
22 |
23 | renderedModal =
24 | }
25 | return {renderedModal}
26 | };
27 |
28 | export default connect(mapState)(ModalManager)
29 |
--------------------------------------------------------------------------------
/src/features/request/RequestDetail/RequestDetailChat.jsx:
--------------------------------------------------------------------------------
1 | import React, { Component } from 'react';
2 | //import { Segment, Header, Comment } from 'semantic-ui-react';
3 | //import { Link } from 'react-router-dom';
4 |
5 | class RequestDetailedChat extends Component {
6 | state = {
7 | showReplyForm: false,
8 | selectedCommentId: null
9 | };
10 | handleOpenReplyForm = id => () => {
11 | this.setState({
12 | showReplyForm: true,
13 | selectedCommentId: id
14 | });
15 | };
16 | handleCloseReplyForm = () => {
17 | this.setState({
18 | selectedCommentId: null,
19 | showReplyForm: false
20 | });
21 | };
22 |
23 | render() {
24 | return (
25 |
26 | Request Chat Detail
27 |
28 | );
29 | }
30 | };
31 |
32 | export default RequestDetailedChat;
33 |
--------------------------------------------------------------------------------
/src/features/session/RalphDetail/SessionDetailMap.jsx:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 | import { Segment, Icon } from 'semantic-ui-react';
3 | import GoogleMapReact from 'google-map-react';
4 |
5 | const Marker = () => ;
6 |
7 | const EventDetailedMap = ({ lat, lng }) => {
8 | const center = [lat, lng];
9 | const zoom = 14;
10 | return (
11 |
12 |
13 |
18 |
19 |
20 |
21 |
22 | );
23 | };
24 |
25 | export default EventDetailedMap;
--------------------------------------------------------------------------------
/src/features/session/SessionDetail/SessionDetailMap.jsx:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 | import { Segment, Icon } from 'semantic-ui-react';
3 | import GoogleMapReact from 'google-map-react';
4 |
5 | const Marker = () => ;
6 |
7 | const EventDetailedMap = ({ lat, lng }) => {
8 | const center = [lat, lng];
9 | const zoom = 14;
10 | return (
11 |
12 |
13 |
18 |
19 |
20 |
21 |
22 | );
23 | };
24 |
25 | export default EventDetailedMap;
--------------------------------------------------------------------------------
/src/app/common/form/DateInput.jsx:
--------------------------------------------------------------------------------
1 | import React from 'react'
2 | import { Form, Label } from 'semantic-ui-react'
3 | import DatePicker from 'react-datepicker'
4 | import 'react-datepicker/dist/react-datepicker.css'
5 | import moment from 'moment'
6 |
7 | const DateInput = ({input: {value, onChange, onBlur, ...restInput}, width, placeholder, meta: {touched, error}, ...rest}) => {
8 | if (value) {
9 | value = moment(value, 'X')
10 | }
11 | return (
12 |
13 |
14 |
21 | {touched && error && }
22 |
23 | )
24 | }
25 |
26 | export default DateInput
27 |
--------------------------------------------------------------------------------
/src/features/user/UserDetailed/UserDetailedPhotos.jsx:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 | import { Grid, Header, Image, Segment } from 'semantic-ui-react';
3 | import LazyLoad from 'react-lazyload';
4 |
5 | const UserDetailedPhotos = ({ photos }) => {
6 | return (
7 |
8 |
9 |
10 |
11 | {photos &&
12 | photos.map(photo => (
13 | }
17 | >
18 |
19 |
20 | ))}
21 |
22 |
23 |
24 | );
25 | };
26 |
27 | export default UserDetailedPhotos;
28 |
--------------------------------------------------------------------------------
/docs/source/testwebapp-quickstart.rst:
--------------------------------------------------------------------------------
1 | testwebapp quickstart
2 | ======================
3 |
4 | Steps
5 | -----
6 |
7 | #. Get project repo gooberu-testwebapp_ ::
8 |
9 | macci:bast23 cat$ git clone https://github.com/gooberu/testwebapp.git
10 | macci:bast23 cat$ cd testwebapp
11 | macci:testwebapp cat$ yarn install
12 | macci:testwebapp cat$ yarn start
13 |
14 | #. Verify application in browser http://localhost:3000/
15 |
16 |
17 | Resources
18 | ---------
19 |
20 | #. Github project gooberu-testwebapp_
21 | #. Visual Studio IDE config for Project visualstudiocode-ide-config_
22 | #. Yarn install yarn-install_
23 |
24 | .. _gooberu-testwebapp: https://github.com/gooberu/testwebapp
25 | .. _visualstudiocode-ide-config: file:///Users/cat/bast23/testwebapp/docs/build/html/testwebapp-dev-detail.html#step-02-testwebapp-checkpoint-02
26 | .. _yarn-install: https://blog.risingstack.com/yarn-vs-npm-node-js-package-managers/
27 |
--------------------------------------------------------------------------------
/src/features/session/SessionList/SessionList.jsx:
--------------------------------------------------------------------------------
1 | import React, { Component } from 'react';
2 | import SessionListItem from './SessionListItem';
3 | import InfiniteScroll from 'react-infinite-scroller';
4 |
5 | class SessionList extends Component {
6 | render() {
7 | const {sessions, getNextSessions, loading, moreSessions } = this.props;
8 | return (
9 |
10 | {sessions && sessions.length !== 0 && (
11 |
17 | {sessions && sessions.map((session) => (
18 |
22 | ))}
23 |
24 | )}
25 |
26 | )
27 | }
28 | }
29 |
30 | export default SessionList
31 |
--------------------------------------------------------------------------------
/docs/make.bat:
--------------------------------------------------------------------------------
1 | @ECHO OFF
2 |
3 | pushd %~dp0
4 |
5 | REM Command file for Sphinx documentation
6 |
7 | if "%SPHINXBUILD%" == "" (
8 | set SPHINXBUILD=sphinx-build
9 | )
10 | set SOURCEDIR=source
11 | set BUILDDIR=build
12 | set SPHINXPROJ=rtddemo
13 |
14 | if "%1" == "" goto help
15 |
16 | %SPHINXBUILD% >NUL 2>NUL
17 | if errorlevel 9009 (
18 | echo.
19 | echo.The 'sphinx-build' command was not found. Make sure you have Sphinx
20 | echo.installed, then set the SPHINXBUILD environment variable to point
21 | echo.to the full path of the 'sphinx-build' executable. Alternatively you
22 | echo.may add the Sphinx directory to PATH.
23 | echo.
24 | echo.If you don't have Sphinx installed, grab it from
25 | echo.http://sphinx-doc.org/
26 | exit /b 1
27 | )
28 |
29 | %SPHINXBUILD% -M %1 %SOURCEDIR% %BUILDDIR% %SPHINXOPTS%
30 | goto end
31 |
32 | :help
33 | %SPHINXBUILD% -M help %SOURCEDIR% %BUILDDIR% %SPHINXOPTS%
34 |
35 | :end
36 | popd
37 |
--------------------------------------------------------------------------------
/src/features/modals/LoginModal.jsx:
--------------------------------------------------------------------------------
1 | import React, {Component} from 'react';
2 | import {Modal} from 'semantic-ui-react';
3 | import {connect} from 'react-redux';
4 | import LoginForm from '../auth/Login/LoginForm';
5 | import {closeModal} from "./modalActions";
6 |
7 | const actions = {closeModal};
8 |
9 | class LoginModal extends Component {
10 | render() {
11 | return (
12 |
17 |
18 | Login to GooberU
19 |
20 |
21 |
22 |
23 |
24 |
25 |
26 | );
27 | }
28 | };
29 |
30 | export default connect(null, actions)(LoginModal);
--------------------------------------------------------------------------------
/src/features/session/RalphDetail/SessionDetailChatForm.jsx:
--------------------------------------------------------------------------------
1 | import React, { Component } from 'react';
2 | import { Form, Button } from 'semantic-ui-react';
3 | import { Field, reduxForm } from 'redux-form';
4 | import TextArea from '../../../app/common/form/TextArea';
5 |
6 | class SessionDetailChatForm extends Component {
7 | handleCommentSubmit = values => {
8 | const { addSessionComment, reset, sessionId, closeForm, parentId } = this.props;
9 | addSessionComment(sessionId, values, parentId);
10 | reset();
11 | if (parentId !== 0) {
12 | closeForm();
13 | }
14 | };
15 |
16 | render() {
17 | return (
18 |
22 | );
23 | }
24 | };
25 |
26 | export default reduxForm({ Fields: 'comment' })(SessionDetailChatForm);
--------------------------------------------------------------------------------
/src/features/session/SessionDetail/SessionDetailChatForm.jsx:
--------------------------------------------------------------------------------
1 | import React, { Component } from 'react';
2 | import { Form, Button } from 'semantic-ui-react';
3 | import { Field, reduxForm } from 'redux-form';
4 | import TextArea from '../../../app/common/form/TextArea';
5 |
6 | class SessionDetailChatForm extends Component {
7 | handleCommentSubmit = values => {
8 | const { addSessionComment, reset, sessionId, closeForm, parentId } = this.props;
9 | addSessionComment(sessionId, values, parentId);
10 | reset();
11 | if (parentId !== 0) {
12 | closeForm();
13 | }
14 | };
15 |
16 | render() {
17 | return (
18 |
22 | );
23 | }
24 | };
25 |
26 | export default reduxForm({ Fields: 'comment' })(SessionDetailChatForm);
--------------------------------------------------------------------------------
/src/features/modals/RegisterModal.jsx:
--------------------------------------------------------------------------------
1 | import React, {Component} from 'react';
2 | import {Modal} from 'semantic-ui-react';
3 | import {connect} from 'react-redux';
4 | import {closeModal} from "./modalActions";
5 | import RegisterForm from "../auth/Register/RegisterForm";
6 |
7 | const actions = {closeModal};
8 |
9 | class RegisterModal extends Component {
10 | render() {
11 | return (
12 |
17 |
18 | GooberU Sign Up!
19 |
20 |
21 |
22 |
23 |
24 |
25 |
26 | );
27 | }
28 | };
29 |
30 | export default connect(null, actions)(RegisterModal);
--------------------------------------------------------------------------------
/src/features/nav/Menus/SignedInMenu.jsx:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 | import { Menu, Image, Dropdown } from 'semantic-ui-react';
3 | import { Link } from 'react-router-dom';
4 |
5 | const SignedInMenu = ({signOut, profile, auth}) => {
6 | return (
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 | );
21 | };
22 |
23 | export default SignedInMenu;
24 |
--------------------------------------------------------------------------------
/src/features/session/sessionReducer.jsx:
--------------------------------------------------------------------------------
1 | import { createReducer } from "../../app/common/util/reducerUtil";
2 | import {
3 | CREATE_SESSION,
4 | DELETE_SESSION,
5 | UPDATE_SESSION,
6 | FETCH_SESSIONS
7 | } from "./sessionConstants";
8 |
9 | const initialState = [];
10 |
11 | export const createSession = (state, payload) => {
12 | return [...state, Object.assign({}, payload.session)];
13 | };
14 |
15 | export const updateSession = (state, payload) => {
16 | return [
17 | ...state.filter(session => session.id !== payload.session.id),
18 | Object.assign({}, payload.session)
19 | ];
20 | };
21 |
22 | export const deleteSession = (state, payload) => {
23 | return [...state.filter(session => session.id !== payload.sessionId)];
24 | };
25 |
26 | export const fetchSessions = (state, payload) => {
27 | return payload.sessions
28 | };
29 |
30 | export default createReducer(initialState, {
31 | [CREATE_SESSION]: createSession,
32 | [UPDATE_SESSION]: updateSession,
33 | [DELETE_SESSION]: deleteSession,
34 | [FETCH_SESSIONS]: fetchSessions
35 | });
36 |
--------------------------------------------------------------------------------
/src/features/subject/subjectReducer.jsx:
--------------------------------------------------------------------------------
1 | import { createReducer } from "../../app/common/util/reducerUtil";
2 | import {
3 | CREATE_SUBJECT,
4 | DELETE_SUBJECT,
5 | UPDATE_SUBJECT,
6 | FETCH_SUBJECTS
7 | } from "./subjectConstants";
8 |
9 | const initialState = [];
10 |
11 | export const createSubject = (state, payload) => {
12 | return [...state, Object.assign({}, payload.subject)];
13 | };
14 |
15 | export const updateSubject = (state, payload) => {
16 | return [
17 | ...state.filter(subject => subject.id !== payload.subject.id),
18 | Object.assign({}, payload.subject)
19 | ];
20 | };
21 |
22 | export const deleteSubject = (state, payload) => {
23 | return [...state.filter(subject => subject.id !== payload.subjectId)];
24 | };
25 |
26 | export const fetchSubjects = (state, payload) => {
27 | return payload.subjects
28 | };
29 |
30 | export default createReducer(initialState, {
31 | [CREATE_SUBJECT]: createSubject,
32 | [UPDATE_SUBJECT]: updateSubject,
33 | [DELETE_SUBJECT]: deleteSubject,
34 | [FETCH_SUBJECTS]: fetchSubjects
35 | });
36 |
--------------------------------------------------------------------------------
/src/features/user/Settings/SettingsNav.jsx:
--------------------------------------------------------------------------------
1 | import React from "react";
2 | import { Grid, Menu, Header } from "semantic-ui-react";
3 | import { NavLink } from "react-router-dom";
4 |
5 | const SettingsNav = () => {
6 | return (
7 |
8 |
20 |
21 |
33 |
34 | );
35 | };
36 |
37 | export default SettingsNav;
38 |
--------------------------------------------------------------------------------
/src/features/request/requestReducer.jsx:
--------------------------------------------------------------------------------
1 | import { createReducer } from "../../app/common/util/reducerUtil";
2 | import {
3 | CREATE_REQUEST,
4 | DELETE_REQUEST,
5 | UPDATE_REQUEST,
6 | FETCH_REQUESTS
7 | } from "./requestConstants";
8 |
9 | const initialState = [];
10 | /*const initialState = {
11 | mytest: 43,
12 | mytestloading: false
13 | };*/
14 |
15 | export const createRequest = (state, payload) => {
16 | return [...state, Object.assign({}, payload.request)];
17 | };
18 |
19 | export const updateRequest = (state, payload) => {
20 | return [
21 | ...state.filter(request => request.id !== payload.request.id),
22 | Object.assign({}, payload.request)
23 | ];
24 | };
25 |
26 | export const deleteRequest = (state, payload) => {
27 | return [...state.filter(request => request.id !== payload.requestId)];
28 | };
29 |
30 | export const fetchRequests = (state, payload) => {
31 | return payload.requests
32 | };
33 |
34 | export default createReducer(initialState, {
35 | [CREATE_REQUEST]: createRequest,
36 | [UPDATE_REQUEST]: updateRequest,
37 | [DELETE_REQUEST]: deleteRequest,
38 | [FETCH_REQUESTS]: fetchRequests
39 | });
40 |
--------------------------------------------------------------------------------
/src/app/reducers/rootReducer.js:
--------------------------------------------------------------------------------
1 | import { combineReducers } from "redux";
2 | import { reducer as FormReducer } from "redux-form";
3 | import { reducer as toastrReducer } from "react-redux-toastr";
4 | import { firebaseReducer } from 'react-redux-firebase';
5 | import { firestoreReducer } from 'redux-firestore';
6 | import testReducer from "../../features/testarea/testReducer";
7 | import modalsReducer from '../../features/modals/modalReducer';
8 | import sessionReducer from "../../features/session/sessionReducer";
9 | import requestReducer from "../../features/request/requestReducer";
10 | import subjectReducer from "../../features/subject/subjectReducer";
11 | import authReducer from "../../features/auth/authReducer";
12 | import asyncReducer from "../../features/async/asyncReducer";
13 |
14 | const rootReducer = combineReducers({
15 | firebase: firebaseReducer,
16 | firestore: firestoreReducer,
17 | form: FormReducer,
18 | test: testReducer,
19 | modals: modalsReducer,
20 | auth: authReducer,
21 | async: asyncReducer,
22 | toastr: toastrReducer,
23 | requests: requestReducer,
24 | sessions: sessionReducer,
25 | subjects: subjectReducer
26 | });
27 |
28 | export default rootReducer;
29 |
--------------------------------------------------------------------------------
/src/features/callRalph/testActions.jsx:
--------------------------------------------------------------------------------
1 | import {
2 | INCREMENT_COUNTER,
3 | DECREMENT_COUNTER,
4 | COUNTER_ACTION_START,
5 | COUNTER_ACTION_DONE
6 | } from './testConstants';
7 |
8 | export const incrementCounter = () => {
9 | return {
10 | type: INCREMENT_COUNTER
11 | }
12 | };
13 |
14 | export const decrementCounter = () => {
15 | return {
16 | type: DECREMENT_COUNTER
17 | }
18 | };
19 |
20 | export const startCounterAction = () => {
21 | return {
22 | type: COUNTER_ACTION_START
23 | }
24 | };
25 |
26 | export const doneCounterAction = () => {
27 | return {
28 | type: COUNTER_ACTION_DONE
29 | }
30 | };
31 |
32 | const delay = (ms) => {
33 | return new Promise(resolve => setTimeout(resolve, ms))
34 | };
35 |
36 | export const incrementAsync = () => {
37 | return async dispatch => {
38 | dispatch(startCounterAction());
39 | await delay(1000);
40 | dispatch({type: INCREMENT_COUNTER});
41 | dispatch(doneCounterAction());
42 | }
43 | };
44 |
45 | export const decrementAsync = () => {
46 | return async dispatch => {
47 | dispatch(startCounterAction());
48 | await delay(1000);
49 | dispatch({type: DECREMENT_COUNTER});
50 | dispatch(doneCounterAction());
51 | }
52 | };
53 |
--------------------------------------------------------------------------------
/src/features/testarea/testActions.jsx:
--------------------------------------------------------------------------------
1 | import {
2 | INCREMENT_COUNTER,
3 | DECREMENT_COUNTER,
4 | COUNTER_ACTION_START,
5 | COUNTER_ACTION_DONE
6 | } from './testConstants';
7 |
8 | export const incrementCounter = () => {
9 | return {
10 | type: INCREMENT_COUNTER
11 | }
12 | };
13 |
14 | export const decrementCounter = () => {
15 | return {
16 | type: DECREMENT_COUNTER
17 | }
18 | };
19 |
20 | export const startCounterAction = () => {
21 | return {
22 | type: COUNTER_ACTION_START
23 | }
24 | };
25 |
26 | export const doneCounterAction = () => {
27 | return {
28 | type: COUNTER_ACTION_DONE
29 | }
30 | };
31 |
32 | const delay = (ms) => {
33 | return new Promise(resolve => setTimeout(resolve, ms))
34 | };
35 |
36 | export const incrementAsync = () => {
37 | return async dispatch => {
38 | dispatch(startCounterAction());
39 | await delay(1000);
40 | dispatch({type: INCREMENT_COUNTER});
41 | dispatch(doneCounterAction());
42 | }
43 | };
44 |
45 | export const decrementAsync = () => {
46 | return async dispatch => {
47 | dispatch(startCounterAction());
48 | await delay(1000);
49 | dispatch({type: DECREMENT_COUNTER});
50 | dispatch(doneCounterAction());
51 | }
52 | };
53 |
--------------------------------------------------------------------------------
/src/features/auth/Login/LoginForm.jsx:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 | import { Form, Segment, Button, Label } from 'semantic-ui-react';
3 | import { connect } from 'react-redux';
4 | import { Field, reduxForm } from 'redux-form';
5 | import TextInput from '../../../app/common/form/TextInput';
6 | import { login, socialLogin } from '../authActions';
7 |
8 | const actions = {
9 | login,
10 | socialLogin
11 | };
12 |
13 | const LoginForm = ({login, handleSubmit, error, socialLogin}) => {
14 | return (
15 |
35 | );
36 | };
37 |
38 | export default connect(null, actions)(reduxForm({form: 'loginForm'})(LoginForm));
--------------------------------------------------------------------------------
/src/features/user/UserDetailed/UserDetailedHeader.jsx:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 | import { Grid, Header, Item, Segment } from 'semantic-ui-react';
3 | import differenceInYears from 'date-fns/difference_in_years';
4 |
5 | const UserDetailedHeader = ({profile}) => {
6 | let age;
7 | if (profile.dateOfBirth) {
8 | age = differenceInYears(Date.now(), profile.dateOfBirth.toDate())
9 | } else {
10 | age = 'unknown age'
11 | }
12 | return (
13 |
14 |
15 |
16 | -
17 |
22 |
23 |
24 |
25 |
26 |
27 | {age}, Lives in {profile.city || 'unknown city'}
28 |
29 |
30 |
31 |
32 |
33 | );
34 | };
35 |
36 | export default UserDetailedHeader;
37 |
--------------------------------------------------------------------------------
/src/features/subject/SubjectList/SubjectList.jsx:
--------------------------------------------------------------------------------
1 | import React, { Component } from "react";
2 | import SubjectListItem from "./SubjectListItem";
3 |
4 | class SubjectList extends Component {
5 | constructor() {
6 | super();
7 | this.state = {
8 | search: ''
9 | }
10 | };
11 |
12 | updateSearch(event) {
13 | this.setState({search: event.target.value.substr(0,20)});
14 | };
15 |
16 | render() {
17 | const { subjects, onSubjectOpen, deleteSubject } = this.props;
18 | // let filteredSubjects = subjects;
19 | let filteredSubjects = subjects.filter(
20 | (subject) => {
21 | return subject.description.indexOf(this.state.search) !== -1;
22 | }
23 | );
24 |
25 | return (
26 |
27 |
Subject List
28 |
33 | {filteredSubjects.map(subject => (
34 |
40 | ))}
41 |
42 | );
43 | }
44 | };
45 |
46 | export default SubjectList;
47 |
--------------------------------------------------------------------------------
/src/features/subject/SubjectList/SubjectListItem.jsx:
--------------------------------------------------------------------------------
1 | import React, { Component } from 'react';
2 | import { Segment, Item, Button } from 'semantic-ui-react';
3 | import { Link } from 'react-router-dom';
4 |
5 | class SubjectListItem extends Component {
6 | render() {
7 | const { subject } = this.props
8 | return (
9 |
10 |
11 |
12 | -
13 |
14 |
15 | {subject.name}
16 |
17 | {subject.category}
18 |
19 | {subject.location}
20 | {/*
21 | GooberU by {subject.hostedBy}
22 | */}
23 |
24 |
25 |
26 |
27 |
28 | {subject.description}
29 |
30 |
31 |
32 | );
33 | }
34 | };
35 |
36 | export default SubjectListItem;
37 |
--------------------------------------------------------------------------------
/src/features/callRalph/testReducer.js:
--------------------------------------------------------------------------------
1 | import {
2 | INCREMENT_COUNTER,
3 | DECREMENT_COUNTER,
4 | COUNTER_ACTION_DONE,
5 | COUNTER_ACTION_START
6 | } from './testConstants';
7 | import { createReducer } from '../../app/common/util/reducerUtil';
8 |
9 | const initialState = {
10 | data: 43,
11 | loading: false
12 | };
13 |
14 | export const incrementCounter = (state, payload) => {
15 | return { ...state, data: state.data + 1 };
16 | };
17 |
18 | export const decrementCounter = (state, payload) => {
19 | return { ...state, data: state.data - 1 };
20 | };
21 |
22 | export const counterActionStart = (state, payload) => {
23 | return {...state, loading: true}
24 | };
25 |
26 | export const counterActionDone = (state, payload) => {
27 | return {...state, loading: false}
28 | };
29 |
30 | // const testReducer = (state = initialState, action) => {
31 | // switch (action.type) {
32 | // case INCREMENT_COUNTER:
33 | // return { ...state, data: state.data + 1 };
34 | // case DECREMENT_COUNTER:
35 | // return { ...state, data: state.data - 1 };
36 | // default:
37 | // return state;
38 | // }
39 | // };
40 |
41 | export default createReducer(initialState, {
42 | [INCREMENT_COUNTER]: incrementCounter,
43 | [DECREMENT_COUNTER]: decrementCounter,
44 | [COUNTER_ACTION_START]: counterActionStart,
45 | [COUNTER_ACTION_DONE]: counterActionDone
46 |
47 | });
48 |
--------------------------------------------------------------------------------
/src/features/testarea/testReducer.js:
--------------------------------------------------------------------------------
1 | import {
2 | INCREMENT_COUNTER,
3 | DECREMENT_COUNTER,
4 | COUNTER_ACTION_DONE,
5 | COUNTER_ACTION_START
6 | } from './testConstants';
7 | import { createReducer } from '../../app/common/util/reducerUtil';
8 |
9 | const initialState = {
10 | data: 43,
11 | loading: false
12 | };
13 |
14 | export const incrementCounter = (state, payload) => {
15 | return { ...state, data: state.data + 1 };
16 | };
17 |
18 | export const decrementCounter = (state, payload) => {
19 | return { ...state, data: state.data - 1 };
20 | };
21 |
22 | export const counterActionStart = (state, payload) => {
23 | return {...state, loading: true}
24 | };
25 |
26 | export const counterActionDone = (state, payload) => {
27 | return {...state, loading: false}
28 | };
29 |
30 | // const testReducer = (state = initialState, action) => {
31 | // switch (action.type) {
32 | // case INCREMENT_COUNTER:
33 | // return { ...state, data: state.data + 1 };
34 | // case DECREMENT_COUNTER:
35 | // return { ...state, data: state.data - 1 };
36 | // default:
37 | // return state;
38 | // }
39 | // };
40 |
41 | export default createReducer(initialState, {
42 | [INCREMENT_COUNTER]: incrementCounter,
43 | [DECREMENT_COUNTER]: decrementCounter,
44 | [COUNTER_ACTION_START]: counterActionStart,
45 | [COUNTER_ACTION_DONE]: counterActionDone
46 |
47 | });
48 |
--------------------------------------------------------------------------------
/src/app/store/configureStore.js:
--------------------------------------------------------------------------------
1 | import { createStore, applyMiddleware } from 'redux';
2 | import { composeWithDevTools } from 'redux-devtools-extension';
3 | import { reactReduxFirebase, getFirebase } from 'react-redux-firebase';
4 | import { reduxFirestore, getFirestore } from 'redux-firestore';
5 | import thunk from 'redux-thunk';
6 | import rootReducer from '../reducers/rootReducer';
7 | import firebase from '../config/firebase';
8 |
9 | const rrfConfig = {
10 | userProfile: 'users',
11 | attachAuthIsReady: true,
12 | useFirestoreForProfile: true,
13 | updateProfileOnLogin: false
14 | };
15 |
16 | export const configureStore = preloadedState => {
17 | const middlewares = [thunk.withExtraArgument({ getFirebase, getFirestore })];
18 | const middlewareEnhancer = applyMiddleware(...middlewares);
19 | const storeEnhancers = [middlewareEnhancer];
20 | const composedEnhancer = composeWithDevTools(
21 | ...storeEnhancers,
22 | reactReduxFirebase(firebase, rrfConfig),
23 | reduxFirestore(firebase)
24 | );
25 | const store = createStore(rootReducer, preloadedState, composedEnhancer);
26 | if (process.env.NODE_ENV !== 'production') {
27 | if (module.hot) {
28 | module.hot.accept('../reducers/rootReducer', () => {
29 | const newRootReducer = require('../reducers/rootReducer').default;
30 | store.replaceReducer(newRootReducer);
31 | });
32 | }
33 | }
34 | return store;
35 | };
36 |
--------------------------------------------------------------------------------
/src/index.js:
--------------------------------------------------------------------------------
1 | import React from "react";
2 | import ReactDOM from "react-dom";
3 | import { BrowserRouter } from "react-router-dom";
4 | import { Provider } from "react-redux";
5 | import "./index.css";
6 | import "semantic-ui-css/semantic.min.css";
7 | import ReduxToastr from "react-redux-toastr";
8 | import "react-redux-toastr/lib/css/react-redux-toastr.min.css";
9 | import App from "./app/layout/App";
10 | import registerServiceWorker from "./registerServiceWorker";
11 | import { configureStore } from "./app/store/configureStore";
12 | import ScrollToTop from './app/common/util/ScrollToTop';
13 | //import { loadSubjects } from './features/subject/subjectActions';
14 |
15 | const store = configureStore();
16 | //store.dispatch(loadSubjects());
17 | const rootEl = document.getElementById("root");
18 |
19 | let render = () => {
20 | ReactDOM.render(
21 |
22 |
23 |
24 |
29 |
30 |
31 |
32 | ,
33 | rootEl
34 | );
35 | };
36 |
37 | if (module.hot) {
38 | module.hot.accept("./app/layout/App", () => {
39 | setTimeout(render);
40 | });
41 | }
42 |
43 | store.firebaseAuthIsReady.then(() => {
44 | render();
45 | registerServiceWorker();
46 | });
47 |
--------------------------------------------------------------------------------
/src/features/home/HomePage.jsx:
--------------------------------------------------------------------------------
1 | import React from 'react'
2 |
3 | const HomePage = ({history}) => {
4 | return (
5 |
6 |
7 |
8 |
9 |
14 |
GooberU
15 |
16 |
??? got a yurn'n for learn'n and earn'n ???
17 |
history.push('/subjects')} className="ui huge white inverted button">
18 | Goober yoUr Subject
19 |
20 |
21 |
22 |
23 |
40 |
41 | );
42 | };
43 |
44 | export default HomePage;
45 |
--------------------------------------------------------------------------------
/src/app/common/form/PlaceInput.jsx:
--------------------------------------------------------------------------------
1 | import React, { Component } from 'react';
2 | import { Form, Label } from 'semantic-ui-react';
3 | import Script from 'react-load-script';
4 | import PlacesAutocomplete from 'react-places-autocomplete';
5 |
6 | const styles = {
7 | autocompleteContainer: {
8 | zIndex: 1000
9 | }
10 | }
11 |
12 | class PlaceInput extends Component {
13 | state = {
14 | scriptLoaded: false
15 | };
16 |
17 | handleScriptLoaded = () => this.setState({ scriptLoaded: true });
18 |
19 | render() {
20 | const {
21 | input,
22 | width,
23 | onSelect,
24 | placeholder,
25 | options,
26 | meta: { touched, error }
27 | } = this.props;
28 | return (
29 |
30 |
34 | {this.state.scriptLoaded && (
35 |
41 | )}
42 | {touched &&
43 | error && (
44 |
47 | )}
48 |
49 | );
50 | }
51 | }
52 |
53 | export default PlaceInput;
54 |
--------------------------------------------------------------------------------
/src/features/session/RalphDetail/SessionDetailSidebar.jsx:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 | import { Segment, List, Label, Item } from 'semantic-ui-react';
3 | import { Link } from 'react-router-dom';
4 |
5 | const SessionDetailSidebar = ({ attendees }) => {
6 | const isHost = false;
7 | return (
8 |
9 |
17 | {attendees && attendees.length} {attendees && attendees.length === 1 ? 'Person' : 'People'} Going
18 |
19 |
20 |
21 | {attendees &&
22 | attendees.map(attendee => (
23 | -
24 | {isHost &&
25 | }
32 |
33 |
34 |
35 | {attendee.name}
36 |
37 |
38 |
39 | ))}
40 |
41 |
42 |
43 | );
44 | };
45 |
46 | export default SessionDetailSidebar;
47 |
--------------------------------------------------------------------------------
/src/features/session/SessionDetail/SessionDetailSidebar.jsx:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 | import { Segment, List, Label, Item } from 'semantic-ui-react';
3 | import { Link } from 'react-router-dom';
4 |
5 | const SessionDetailSidebar = ({ attendees }) => {
6 | const isHost = false;
7 | return (
8 |
9 |
17 | {attendees && attendees.length} {attendees && attendees.length === 1 ? 'Person' : 'People'} Going
18 |
19 |
20 |
21 | {attendees &&
22 | attendees.map(attendee => (
23 | -
24 | {isHost &&
25 | }
32 |
33 |
34 |
35 | {attendee.name}
36 |
37 |
38 |
39 | ))}
40 |
41 |
42 |
43 | );
44 | };
45 |
46 | export default SessionDetailSidebar;
47 |
--------------------------------------------------------------------------------
/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "revents",
3 | "version": "0.1.0",
4 | "private": true,
5 | "dependencies": {
6 | "cuid": "^2.1.1",
7 | "date-fns": "^1.29.0",
8 | "firebase": "^4.12.1",
9 | "google-map-react": "^0.34.0",
10 | "history": "^4.7.2",
11 | "moment": "^2.22.0",
12 | "react": "^16.3.0",
13 | "react-cropper": "^1.0.1",
14 | "react-datepicker": "^1.4.0",
15 | "react-dom": "^16.3.0",
16 | "react-dropzone": "^4.2.9",
17 | "react-geolocated": "^2.4.0",
18 | "react-infinite-scroller": "^1.1.3",
19 | "react-lazyload": "^2.3.0",
20 | "react-load-script": "0.0.6",
21 | "react-loadable": "^5.3.1",
22 | "react-places-autocomplete": "^6.1.3",
23 | "react-redux": "^5.0.7",
24 | "react-redux-firebase": "^2.0.6",
25 | "react-redux-toastr": "^7.2.3",
26 | "react-router-dom": "^4.2.2",
27 | "react-router-redux": "^5.0.0-alpha.9",
28 | "react-scripts": "1.1.2",
29 | "redux": "^3.7.2",
30 | "redux-auth-wrapper": "^2.0.2",
31 | "redux-firestore": "^0.3.2",
32 | "redux-form": "^7.3.0",
33 | "redux-thunk": "^2.2.0",
34 | "revalidate": "^1.2.0",
35 | "semantic-ui-css": "2.2.14",
36 | "semantic-ui-react": "^0.79.0"
37 | },
38 | "scripts": {
39 | "start": "react-scripts start",
40 | "build": "react-scripts build",
41 | "test": "react-scripts test --env=jsdom",
42 | "eject": "react-scripts eject",
43 | "analyze": "source-map-explorer build/static/js/main.*",
44 | "deploy": "npm run build && firebase deploy"
45 | },
46 | "devDependencies": {
47 | "redux-devtools-extension": "^2.13.2",
48 | "source-map-explorer": "^1.5.0"
49 | }
50 | }
51 |
--------------------------------------------------------------------------------
/src/features/user/UserDetailed/UserDetailedDescription.jsx:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 | import { Grid, Header, Icon, Item, List, Segment } from 'semantic-ui-react';
3 | import format from 'date-fns/format';
4 |
5 | const UserDetailedDescription = ({ profile }) => {
6 | let createdAt;
7 | if (profile.createdAt) {
8 | createdAt = format(profile.createdAt.toDate(), 'D MMM YYYY');
9 | }
10 | return (
11 |
12 |
13 |
14 |
15 |
16 |
17 | I am a: {profile.occupation || 'tbn'}
18 |
19 |
20 | Originally from {profile.origin || 'tbn'}
21 |
22 |
23 | Member Since: {createdAt}
24 |
25 | {profile.description}
26 |
27 |
28 |
29 | {profile.interests ?
30 |
31 | {profile.interests &&
32 | profile.interests.map((interest, index) => (
33 | -
34 |
35 | {interest}
36 |
37 | ))}
38 |
: No interests
}
39 |
40 |
41 |
42 |
43 | );
44 | };
45 |
46 | export default UserDetailedDescription;
47 |
--------------------------------------------------------------------------------
/public/index.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
11 |
12 |
13 |
22 | GooberU App
23 |
24 |
25 |
28 |
29 |
39 |
40 |
41 |
--------------------------------------------------------------------------------
/src/features/user/Settings/SettingsDashboard.jsx:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 | import { connect } from 'react-redux';
3 | import { Grid } from 'semantic-ui-react';
4 | import { Switch, Route, Redirect } from 'react-router-dom';
5 | import SettingsNav from './SettingsNav';
6 | import AboutPage from './AboutPage';
7 | import PhotosPage from './PhotosPage';
8 | import AccountPage from './AccountPage';
9 | import BasicPage from './BasicPage';
10 | import { updatePassword } from '../../auth/authActions';
11 | import { updateProfile } from '../userActions';
12 |
13 | const actions = {
14 | updatePassword,
15 | updateProfile
16 | };
17 | const mapState = (state) => ({
18 | providerId: state.firebase.auth.providerData[0].providerId,
19 | user: state.firebase.profile
20 | });
21 | const SettingsDashboard = ({ updatePassword, providerId, user, updateProfile }) => {
22 | return (
23 |
24 |
25 |
26 |
27 | } />
28 | } />
29 |
30 | }
33 | />
34 |
35 |
36 |
37 |
38 |
39 |
40 | );
41 | };
42 |
43 | export default connect(mapState, actions)(SettingsDashboard);
44 |
--------------------------------------------------------------------------------
/src/features/user/UserDetailed/UserDetailedSessions.jsx:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 | import { Card, Grid, Header, Image, Segment, Tab } from 'semantic-ui-react';
3 | import { Link } from 'react-router-dom';
4 | import format from 'date-fns/format';
5 |
6 | const panes = [
7 | {menuItem: 'All Sessions', pane: {key: 'allSessions'}},
8 | {menuItem: 'Past Sessions', pane: {key: 'pastSessions'}},
9 | {menuItem: 'Future Sessions', pane: {key: 'futureSessions'}},
10 | {menuItem: 'Hosting', pane: {key: 'hosted'}},
11 | ];
12 | const UserDeteiledSessions = ({ sessions, sessionsLoading, changeTab }) => {
13 | return (
14 |
15 |
16 |
17 | changeTab(e, data)} panes={panes} menu={{secondary: true, pointing: true}}/>
18 |
19 |
20 |
21 | {sessions &&
22 | sessions.map(session => (
23 |
24 |
25 |
26 | {session.title}
27 |
28 | {format(session.date.toDate(), 'DD MMM YYYY')}
29 | {format(session.date.toDate(), 'h:mm A')}
30 |
31 |
32 |
33 | ))}
34 |
35 |
36 |
37 | );
38 | };
39 |
40 | export default UserDeteiledSessions;
41 |
--------------------------------------------------------------------------------
/src/features/auth/Register/RegisterForm.jsx:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 | import { connect } from 'react-redux';
3 | import { Form, Segment, Button, Label } from 'semantic-ui-react';
4 | import { Field, reduxForm } from 'redux-form';
5 | import { combineValidators, isRequired } from 'revalidate';
6 | import TextInput from '../../../app/common/form/TextInput';
7 | import { registerUser } from '../authActions';
8 | import SocialLogin from '../SocialLogin/SocialLogin';
9 |
10 | const actions = {
11 | registerUser
12 | };
13 |
14 | const validate = combineValidators({
15 | displayName: isRequired('displayName'),
16 | email: isRequired('email'),
17 | password: isRequired('password')
18 | });
19 |
20 | const RegisterForm = ({registerUser, handleSubmit, error, invalid, submitting}) => {
21 | return (
22 |
23 |
50 |
51 | );
52 | };
53 |
54 | export default connect(null, actions)(reduxForm({form: 'registerForm', validate})(RegisterForm));
55 |
--------------------------------------------------------------------------------
/src/app/common/util/helpers.js:
--------------------------------------------------------------------------------
1 | import moment from 'moment';
2 |
3 | export const objectToArray = (object) => {
4 | if (object) {
5 | return Object.entries(object).map(e => Object.assign(e[1], {id: e[0]}))
6 | }
7 | };
8 |
9 | export const createNewSubject = (subject) => {
10 | subject.date = moment(subject.date).toDate();
11 | return {
12 | ...subject,
13 | // date: Date.now()
14 | }
15 | };
16 |
17 | export const createNewSession = (user, photoURL, session) => {
18 | session.date = moment(session.date).toDate();
19 | return {
20 | ...session,
21 | hostUid: user.uid,
22 | hostedBy: user.displayName,
23 | hostPhotoURL: photoURL || '/assets/user.png',
24 | created: Date.now(),
25 | attendees: {
26 | [user.uid]: {
27 | going: true,
28 | joinDate: Date.now(),
29 | photoURL: photoURL || '/assets/user.png',
30 | displayName: user.displayName,
31 | host: true
32 | }
33 | }
34 | }
35 | };
36 |
37 | export const createNewRequest = (user, photoURL, request) => {
38 | request.date = moment(request.date).toDate();
39 | return {
40 | ...request,
41 | hostUid: user.uid,
42 | hostedBy: user.displayName,
43 | hostPhotoURL: photoURL || '/assets/user.png',
44 | created: Date.now(),
45 | attendees: {
46 | [user.uid]: {
47 | going: true,
48 | joinDate: Date.now(),
49 | photoURL: photoURL || '/assets/user.png',
50 | displayName: user.displayName,
51 | host: true
52 | }
53 | }
54 | }
55 | };
56 |
57 | export const createDataTree = dataset => {
58 | let hashTable = Object.create(null);
59 | dataset.forEach(a => hashTable[a.id] = {...a, childNodes: []});
60 | let dataTree = [];
61 | dataset.forEach(a => {
62 | if (a.parentId) hashTable[a.parentId].childNodes.push(hashTable[a.id]);
63 | else dataTree.push(hashTable[a.id])
64 | });
65 | return dataTree
66 | };
67 |
--------------------------------------------------------------------------------
/src/features/session/RalphDetail/SessionDetailHeader.jsx:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 | import { Segment, Image, Item, Header, Button } from 'semantic-ui-react';
3 | import { Link } from 'react-router-dom';
4 | import format from 'date-fns/format';
5 |
6 | const sessionImageStyle = {
7 | filter: 'brightness(30%)'
8 | };
9 | const sessionImageTextStyle = {
10 | position: 'absolute',
11 | bottom: '5%',
12 | left: '5%',
13 | width: '100%',
14 | height: 'auto',
15 | color: 'white'
16 | };
17 | const SessionDetailHeader = ({session, isHost, isGoing, goingToSession, cancelGoingToSession}) => {
18 | let sessionDate;
19 | if (session.date) {
20 | sessionDate = session.date.toDate();
21 | }
22 | return (
23 |
24 |
25 |
26 |
27 |
28 | -
29 |
30 |
35 |
{format(sessionDate, 'dddd Do MMMM')}
36 |
37 | Hosted by {session.hostedBy}
38 |
39 |
40 |
41 |
42 |
43 |
44 |
45 | {!isHost && (
46 |
47 | {isGoing ? (
48 |
49 | ) : (
50 |
51 | )}
52 |
53 | )}
54 | {isHost && (
55 |
58 | )}
59 |
60 |
61 | );
62 | };
63 |
64 | export default SessionDetailHeader;
65 |
--------------------------------------------------------------------------------
/src/features/session/SessionDetail/SessionDetailHeader.jsx:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 | import { Segment, Image, Item, Header, Button } from 'semantic-ui-react';
3 | import { Link } from 'react-router-dom';
4 | import format from 'date-fns/format';
5 |
6 | const sessionImageStyle = {
7 | filter: 'brightness(30%)'
8 | };
9 | const sessionImageTextStyle = {
10 | position: 'absolute',
11 | bottom: '5%',
12 | left: '5%',
13 | width: '100%',
14 | height: 'auto',
15 | color: 'white'
16 | };
17 | const SessionDetailHeader = ({session, isHost, isGoing, goingToSession, cancelGoingToSession}) => {
18 | let sessionDate;
19 | if (session.date) {
20 | sessionDate = session.date.toDate();
21 | }
22 | return (
23 |
24 |
25 |
26 |
27 |
28 | -
29 |
30 |
35 |
{format(sessionDate, 'dddd Do MMMM')}
36 |
37 | Hosted by {session.hostedBy}
38 |
39 |
40 |
41 |
42 |
43 |
44 |
45 | {!isHost && (
46 |
47 | {isGoing ? (
48 |
49 | ) : (
50 |
51 | )}
52 |
53 | )}
54 | {isHost && (
55 |
58 | )}
59 |
60 |
61 | );
62 | };
63 |
64 | export default SessionDetailHeader;
65 |
--------------------------------------------------------------------------------
/src/features/session/SessionList/SessionListItem.jsx:
--------------------------------------------------------------------------------
1 | import React, { Component } from 'react';
2 | import { Segment, Item, Icon, List, Button, Label } from 'semantic-ui-react';
3 | import { Link } from 'react-router-dom';
4 | import format from 'date-fns/format';
5 | import SessionListAttendee from './SessionListAttendee';
6 | import { objectToArray } from '../../../app/common/util/helpers';
7 |
8 | class SessionListItem extends Component {
9 | render() {
10 | const {session /*, deleteSession */} = this.props
11 | return (
12 |
13 |
14 |
15 | -
16 |
17 |
18 | {session.title}
19 |
20 | GooberU by {session.hostedBy}
21 |
22 | {session.cancelled &&
23 | }
24 |
25 |
26 |
27 |
28 |
29 |
30 | {format(session.date.toDate(), 'dddd Do MMMM')} at {format(session.date.toDate(), 'HH:mm')}|
31 | {session.venue}
32 |
33 |
34 |
35 |
36 | {session.attendees && objectToArray(session.attendees).map((attendee) => (
37 |
38 | ))}
39 |
40 |
41 |
42 | {session.description}
43 | {/**/}
44 |
45 |
46 |
47 | );
48 | }
49 | };
50 |
51 | export default SessionListItem;
52 |
--------------------------------------------------------------------------------
/src/features/request/RequestDetail/RequestDetailPage.jsx:
--------------------------------------------------------------------------------
1 | import React, { Component } from 'react';
2 | import { Grid } from 'semantic-ui-react';
3 | import { connect } from 'react-redux';
4 | import { withFirestore, isEmpty, firebaseConnect } from 'react-redux-firebase';
5 | import RequestDetailChat from './RequestDetailChat';
6 | import { compose } from 'redux';
7 | import { objectToArray, createDataTree } from '../../../app/common/util/helpers';
8 | import { goingToSession, cancelGoingToSession } from '../../user/userActions';
9 | import { addRequestComment } from '../requestActions';
10 |
11 | const mapState = (state, ownProps) => {
12 | let request = {};
13 | if (state.firestore.ordered.requests && state.firestore.ordered.requests[0]) {
14 | request = state.firestore.ordered.requests[0];
15 | }
16 | return {
17 | request,
18 | auth: state.firebase.auth,
19 | requestChat:
20 | !isEmpty(state.firebase.data.request_chat) &&
21 | objectToArray(state.firebase.data.request_chat[ownProps.match.params.id])
22 | };
23 | };
24 | const actions = {
25 | goingToSession,
26 | cancelGoingToSession,
27 | addRequestComment
28 | };
29 |
30 | class RequestDetailPage extends Component {
31 | async componentDidMount() {
32 | const { firestore, match } = this.props;
33 | await firestore.setListener(`requests/${match.params.id}`);
34 | };
35 | async componentWillUnmount() {
36 | const { firestore, match } = this.props;
37 | await firestore.unsetListener(`requests/${match.params.id}`);
38 | };
39 |
40 | render() {
41 | const { request, auth, goingToSession, cancelGoingToSession, addSessionComment, sessionChat } = this.props;
42 | const attendees =
43 | request && request.attendees && objectToArray(request.attendees);
44 | const isHost = request.hostUid === auth.uid;
45 | const isGoing = attendees && attendees.some(a => a.id === auth.uid);
46 | const chatTree = !isEmpty(sessionChat) && createDataTree(sessionChat);
47 | return (
48 |
49 | );
50 | }
51 | };
52 |
53 | export default compose(
54 | withFirestore,
55 | connect(mapState, actions),
56 | firebaseConnect(props => [`session_chat/${props.match.params.id}`])
57 | )(RequestDetailPage);
58 |
--------------------------------------------------------------------------------
/src/index.css:
--------------------------------------------------------------------------------
1 | body {
2 | background-color: rgb(234, 234, 234) !important;
3 | }
4 |
5 | /*timepicker style*/
6 | .react-datepicker__time-container .react-datepicker__time .react-datepicker__time-box ul.react-datepicker__time-list {
7 | padding-left: 0;
8 | padding-right: 0;
9 | width: 100px;
10 | }
11 |
12 | .react-datepicker__input-container {
13 | width: inherit;
14 | }
15 |
16 | .react-datepicker-wrapper {
17 | width: 100%;
18 | }
19 |
20 |
21 | /*home page styles*/
22 |
23 | .masthead {
24 | background-image: linear-gradient(
25 | 135deg,
26 | rgb(24, 42, 115) 0%,
27 | rgb(33, 138, 174) 69%,
28 | rgb(32, 167, 172) 89%
29 | ) !important;
30 | }
31 |
32 | .masthead.segment {
33 | min-height: 700px;
34 | padding: 1em 0 !important;
35 | }
36 |
37 | .masthead .ui.menu .ui.button,
38 | .ui.menu a.ui.inverted.button {
39 | margin-left: 0.5em;
40 | }
41 |
42 | .masthead h1.ui.header {
43 | margin-top: 3em;
44 | margin-bottom: 0;
45 | font-size: 4em;
46 | font-weight: normal;
47 | }
48 |
49 | .masthead h2 {
50 | font-size: 1.7em;
51 | font-weight: normal;
52 | }
53 |
54 | .footer.segment {
55 | padding: 5em 0;
56 | }
57 |
58 | .secondary.inverted.pointing.menu {
59 | border: none;
60 | }
61 |
62 | /*end home page styles*/
63 |
64 | /* navbar styles */
65 |
66 | .ui.menu .item img.logo {
67 | margin-right: 1.5em;
68 | }
69 |
70 | .ui.fixed.menu {
71 | background-image: linear-gradient(
72 | 135deg,
73 | rgb(24, 42, 115) 0%,
74 | rgb(33, 138, 174) 69%,
75 | rgb(32, 167, 172) 89%
76 | ) !important;
77 | }
78 |
79 | .ui.main.container,
80 | .main.segment {
81 | margin-top: 7em;
82 | }
83 |
84 | .ui.center.aligned.segment.attendance-preview {
85 | background-color: #f5f5f5;
86 | }
87 |
88 | .masthead .ui.menu .ui.button,
89 | .ui.menu a.ui.inverted.button {
90 | margin-left: 0.5em;
91 | }
92 |
93 | .ui.menu .item>img:not(.ui) {
94 | margin-right: 1.5em !important;
95 | }
96 |
97 | .ui.menu:not(.vertical) .item>.button {
98 | margin-left: 0.5em;
99 | }
100 |
101 | /*chat comments*/
102 |
103 | .ui.comments .comment .comments {
104 | padding-bottom: 0 !important;
105 | padding-left: 2em !important;
106 | }
107 |
108 |
--------------------------------------------------------------------------------
/src/features/session/RalphDetail/SessionDetailInfo.jsx:
--------------------------------------------------------------------------------
1 | import React, { Component } from 'react';
2 | import { Segment, Grid, Icon, Button } from 'semantic-ui-react';
3 | import SessionDetailMap from './SessionDetailMap';
4 | import format from 'date-fns/format';
5 |
6 | class SessionDetailInfo extends Component {
7 | state = {
8 | showMap: false
9 | };
10 | componentWillUnmount() {
11 | this.setState({
12 | showMap: false
13 | })
14 | };
15 | showMapToggle = () => {
16 | this.setState(prevState => ({
17 | showMap: !prevState.showMap
18 | }))
19 | };
20 |
21 | render() {
22 | const { session } = this.props;
23 | let sessionDate;
24 | if (session.date) {
25 | sessionDate = session.date.toDate();
26 | }
27 | return (
28 |
29 |
30 |
31 |
32 |
33 |
34 |
35 | {session.description}
36 |
37 |
38 |
39 |
40 |
41 |
42 |
43 |
44 |
45 | {format(sessionDate, 'dddd Do MMM')} at {format(sessionDate, 'h:mm A')}
46 |
47 |
48 |
49 |
50 |
51 |
52 |
53 |
54 |
55 | {session.venue}
56 |
57 |
58 |
59 |
60 |
61 |
62 | {this.state.showMap &&
63 | }
64 |
65 | );
66 | }
67 | };
68 |
69 | export default SessionDetailInfo;
70 |
--------------------------------------------------------------------------------
/src/features/session/SessionDetail/SessionDetailInfo.jsx:
--------------------------------------------------------------------------------
1 | import React, { Component } from 'react';
2 | import { Segment, Grid, Icon, Button } from 'semantic-ui-react';
3 | import SessionDetailMap from './SessionDetailMap';
4 | import format from 'date-fns/format';
5 |
6 | class SessionDetailInfo extends Component {
7 | state = {
8 | showMap: false
9 | };
10 | componentWillUnmount() {
11 | this.setState({
12 | showMap: false
13 | })
14 | };
15 | showMapToggle = () => {
16 | this.setState(prevState => ({
17 | showMap: !prevState.showMap
18 | }))
19 | };
20 |
21 | render() {
22 | const { session } = this.props;
23 | let sessionDate;
24 | if (session.date) {
25 | sessionDate = session.date.toDate();
26 | }
27 | return (
28 |
29 |
30 |
31 |
32 |
33 |
34 |
35 | {session.description}
36 |
37 |
38 |
39 |
40 |
41 |
42 |
43 |
44 |
45 | {format(sessionDate, 'dddd Do MMM')} at {format(sessionDate, 'h:mm A')}
46 |
47 |
48 |
49 |
50 |
51 |
52 |
53 |
54 |
55 | {session.venue}
56 |
57 |
58 |
59 |
60 |
61 |
62 | {this.state.showMap &&
63 | }
64 |
65 | );
66 | }
67 | };
68 |
69 | export default SessionDetailInfo;
70 |
--------------------------------------------------------------------------------
/src/features/user/Settings/BasicPage.jsx:
--------------------------------------------------------------------------------
1 | import React, { Component } from 'react';
2 | import { Segment, Form, Header, Divider, Button } from 'semantic-ui-react';
3 | import { Field, reduxForm } from 'redux-form';
4 | import moment from 'moment';
5 | import DateInput from '../../../app/common/form/DateInput';
6 | import PlaceInput from '../../../app/common/form/PlaceInput';
7 | import TextInput from '../../../app/common/form/TextInput';
8 | import RadioInput from '../../../app/common/form/RadioInput';
9 |
10 | class BasicPage extends Component {
11 | render() {
12 | const { pristine, submitting, handleSubmit, updateProfile } = this.props;
13 | return (
14 |
15 |
16 |
25 |
26 |
33 |
40 |
41 |
52 |
60 |
61 |
67 |
68 |
69 | );
70 | }
71 | };
72 |
73 | export default reduxForm({ form: 'userProfile', enableReinitialize: true, destroyOnUnmount: false })(
74 | BasicPage
75 | );
76 |
--------------------------------------------------------------------------------
/src/features/nav/NavBar/NavBar.jsx:
--------------------------------------------------------------------------------
1 | import React, { Component } from 'react';
2 | import { connect } from 'react-redux';
3 | import { withFirebase } from 'react-redux-firebase';
4 | import { Menu, Container, Button } from 'semantic-ui-react';
5 | import { NavLink, Link, withRouter } from 'react-router-dom';
6 | import SignedOutMenu from '../Menus/SignedOutMenu';
7 | import SignedInMenu from '../Menus/SignedInMenu';
8 | import { openModal } from '../../modals/modalActions';
9 |
10 | const actions = {
11 | openModal
12 | };
13 |
14 | const mapState = (state) => ({
15 | auth: state.firebase.auth,
16 | profile: state.firebase.profile
17 | });
18 |
19 | class NavBar extends Component {
20 | handleSignIn = () => {
21 | this.props.openModal('LoginModal')
22 | };
23 | handleRegister = () => {
24 | this.props.openModal('RegisterModal')
25 | }
26 | handleSignOut = () => {
27 | this.props.firebase.logout();
28 | this.props.history.push('/')
29 | };
30 | render() {
31 | const { auth, profile} = this.props;
32 | const authenticated = auth.isLoaded && !auth.isEmpty
33 | return (
34 |
72 | );
73 | }
74 | };
75 |
76 | export default withRouter(withFirebase(connect(mapState, actions)(NavBar)));
77 |
--------------------------------------------------------------------------------
/src/features/user/UserDetailed/UserDetailedPage.jsx:
--------------------------------------------------------------------------------
1 | import React, { Component } from 'react';
2 | import { Grid } from 'semantic-ui-react';
3 | import { connect } from 'react-redux';
4 | import { firestoreConnect, isEmpty } from 'react-redux-firebase';
5 | import { compose } from 'redux';
6 | import UserDetailedHeader from './UserDetailedHeader';
7 | import UserDetailedDescription from './UserDetailedDescription';
8 | import UserDetailedPhotos from './UserDetailedPhotos';
9 | import UserDetailedSidebar from './UserDetailedSidebar';
10 | import UserDetailedSessions from './UserDetailedSessions';
11 | import { userDetailedQuery } from '../userQueries';
12 | import LoadingComponent from '../../../app/layout/LoadingComponent';
13 | import { getUserSessions } from '../userActions';
14 |
15 | const mapState = (state, ownProps) => {
16 | let userUid = null;
17 | let profile = {};
18 | if (ownProps.match.params.id === state.auth.uid) {
19 | profile = state.firebase.profile
20 | } else {
21 | profile = !isEmpty(state.firestore.ordered.profile) && state.firestore.ordered.profile[0];
22 | userUid = ownProps.match.params.id;
23 | }
24 | return {
25 | profile,
26 | userUid,
27 | sessions: state.sessions,
28 | sessionsLoading: state.async.loading,
29 | auth: state.firebase.auth,
30 | photos: state.firestore.ordered.photos,
31 | requesting: state.firestore.status.requesting
32 | }
33 | };
34 | const actions = {
35 | getUserSessions
36 | };
37 |
38 | class UserDetailedPage extends Component {
39 | async componentDidMount() {
40 | let sessions = await this.props.getUserSessions(this.props.userUid);
41 | console.log(sessions);
42 | };
43 | changeTab = (e, data) => {
44 | this.props.getUserSessions(this.props.userUid, data.activeIndex)
45 | };
46 |
47 | render() {
48 | const {profile, photos, auth, match, requesting, sessions, sessionsLoading} = this.props;
49 | const isCurrentUser = auth.uid === match.params.id;
50 | const loading = Object.values(requesting).some(a => a === true);
51 |
52 | if (loading) return
53 | return (
54 |
55 |
56 |
57 |
58 | {photos && photos.length > 0 &&
59 | }
60 |
61 |
62 | );
63 | }
64 | };
65 |
66 | export default compose(
67 | connect(mapState, actions),
68 | firestoreConnect((auth, userUid) => userDetailedQuery(auth, userUid)),
69 | )(UserDetailedPage);
70 |
--------------------------------------------------------------------------------
/docs/source/testwebapp-fork-acceptance.rst:
--------------------------------------------------------------------------------
1 | testwebapp fork acceptance
2 | ==========================
3 |
4 | Feature: FA-001 - GooberU Subject Search
5 | ----------------------------------------
6 |
7 | As a GooberU Community User
8 | I want a GooberU-Portal subject search page
9 | So I can connect with a tutor
10 |
11 | Scenario: GooberU-Search Page
12 | Given I open the url "http://localhost:3000/"
13 | Then I expect that the url is "http://localhost:3000/"
14 | And I expect that the title is "GooberU App"
15 | And I expect a request input with title "REQUEST A TUTOR"
16 |
17 | Scenario Outline: GooberU-Search
18 | Given I open the url ""
19 | Then I expect that the url is ""
20 | And I expect that the title is ""
21 | And I expect a request input with placeholder "Your Request here..."
22 | And I expect that button "