# Rock Paper Scissors UI

This is the web UI client for Rock Paper Scissors.  It is written in React and was bootstrapped with the [React Redux Starter Kit](https://github.com/davezuko/react-redux-starter-kit).

## Requirements
* node `^4.5.0`
* npm `^3.0.0`
* yarn `^0.18.1`

## Installation:
Follow the guides below to setup the above requirements:

* [Node & NPM Guide](https://docs.npmjs.com/getting-started/installing-node)
* [Yarn Guide](https://yarnpkg.com/en/docs/install)


## Usage
After cloning the project, simply run the following in the project directory:

```bash
$ yarn install   # Install project dependencies
$ npm start      # Compile and launch
```

If everything works, then you should be able to access the app at:  `localhost:3000`.

While developing, you will probably rely mostly on `npm start`; however, there are additional scripts at your disposal:

|`npm run <script>`|Description|
|------------------|-----------|
|`start`|Serves your app at `localhost:3000`. HMR will be enabled in development.|
|`compile`|Compiles the application to disk (`~/dist` by default).|
|`dev`|Same as `npm start`, but enables nodemon for the server as well.|
|`test`|Runs unit tests with Karma and generates a coverage report.|
|`test:dev`|Runs Karma and watches for changes to re-run tests; does not generate coverage reports.|
|`deploy`|Runs linter, tests, and then, on success, compiles your application to disk.|
|`deploy:dev`|Same as `deploy` but overrides `NODE_ENV` to "development".|
|`deploy:prod`|Same as `deploy` but overrides `NODE_ENV` to "production".|
|`lint`|Lint all `.js` files.|
|`lint:fix`|Lint and fix all `.js` files. [Read more on this](http://eslint.org/docs/user-guide/command-line-interface.html#fix).|

## Deployment
Out of the box, this starter kit is deployable by serving the `~/dist` folder generated by `npm run deploy` (make sure to specify your target `NODE_ENV` as well).

## Resources
React, Redux, React-Router, and JavaScript Promises are the major concepts that power this web-app.  If you're unfamiliar with these 3 packages, then I suggest you check out the links below to get an understanding of how they work:

* [React Documentation](https://facebook.github.io/react/docs/hello-world.html)
* [Redux Documentation](http://redux.js.org/)
* [React-Router Guide](https://github.com/ReactTraining/react-router/tree/master/docs)
* [JavaScript Promises Intro](https://developers.google.com/web/fundamentals/getting-started/primers/promises)

## Application Structure
The application structure somewhat follows the [fractal structure](https://github.com/davezuko/react-redux-starter-kit/wiki/Fractal-Project-Structure).  In this structure, functionality is grouped primarily by feature rather than file type.  Below is our structure:

```
.
├── bin                      # Build/Start scripts
├── config                   # Project and build configurations
├── docs                     # Documentation and tutorials
├── public                   # Static public assets
├── server                   # Express application that provides webpack middleware
│   └── main.js              # Server application entry point
├── src                      # Application source code
│   ├── index.html           # Main HTML page container for app
│   ├── main.js              # Application bootstrap and rendering
│   ├── components           # Global Reusable Presentational & Container Components
│   ├── modules              # Global Reusable collections of reducers/actions
│   │   ├── api_data         # Module for handling data returned from API calls
│   │   └── user_session     # Module for anything related to current user data
│   ├── layouts              # Components that dictate major page structure
│   │   ├── CoreLayout       # Defines page layout that all routes render within
│   ├── routes               # Main route definitions and async split points
│   │   ├── index.js         # Bootstraps the main application routes with store
│   │   ├── Metrics          # A route within our application (in this case '/metrics')
│   │   │   ├── index.js     # Route definition (passed to CoreLayout's router)
│   │   │   ├── components   # Presentational Components for this route
│   │   │   ├── containers   # Connects components to actions and store
│   │   │   ├── actions      # Collection of actions for this route
│   │   │   ├── reducers     # Collection of reducers for this route
│   │   │   ├── routes       # Routes may contain other routes within (i.e. '/metrics/graph')
│   │   │   └── assets       # Assets required to render components
│   ├── store                # Redux-specific pieces
│   │   ├── createStore.js   # Create and instrument redux store
│   │   └── reducers.js      # Reducer registry and injection
│   ├── styles               # Application-wide styles (generally settings)
│   └── utils                # Global misc. Util classes
│       └── ApiUtils.js      # Actual API Calls to the server
└── tests                    # Unit tests
```

Although there are many directories and files; for the most part, while developing, you will be working in the `src` directory - particularly within the `src/routes` and `src/modules` which are explained in greater detail below.

### Routes

Simply put, a route is a page in our application.  Everything within a route's directory is what's needed to power that route and bring it to life.  Typically the contents of a route's directory **should not be accessed by other non-child routes** or other parts of the app.  However, routes are welcome to use global stuff such as modules, components, and utils.

### Modules

Modules are anything that alter the Redux state of our application - typically this happens when we make an API call to the server and get back a response.

Recall that in Redux, the store manages our `state` and that our state is nothing more than a JS object with key/value pairs.  Request to handle changes to our application's state are handled via `actions`, while how those changes are received and represented in our state are determined via `reducers`.  A module is simply a collection of actions and reducers.

Note that in our application structure there are both *top-level modules* and *route-level modules*.  State data handled by our top-level modules are available to all routes and components, while anything within a route-level module is restricted to only that route.

For convenience sake, the action and reducers of the modules in routes are collapsed into their directory structure (i.e. there is no sub-directory for modules in a route).

### Containers

A route will direct a url-path to a given react component.  Within that component you can render additional components.  Typically our components (including the root component we route to) are wrapped by a `container`.  A container is nothing more than a *dummy* component that *contains* the component we really want to render.  The container's job is to map values in the Redux state of our app to properties in our components.  

By doing this, we reduce the complexity of our components and separate the data-handling from the rendering as best as we can.  This concept is commonly practiced in React development, and you can read more about it [here](https://medium.com/@dan_abramov/smart-and-dumb-components-7ca2f9a7c7d0#.fi16ugdre) and [here](https://medium.com/@learnreact/container-components-c0e67432e005#.frkzv287u).

## Application State Structure

Our state is managed by our store and one of the key concepts of the Redux design pattern.  It's a set of nested JS objects that represent the state of our application at a given time.  The below graph represents the structure of our state.  The key's within our state are broken down into two types:

* **global:** Information shared across routes and accessible by all.
* **route:** Information related to a specific route.

![](docs/rps-state-graph.png)

For example, loading the `metrics_table` route will make an API call that populates our `api_data` with `buckets`, `metrics`, `ranges`, etc.  The metrics table can that get a list of the metrics by querying `state.api_data.metricIds`.  Now if a user navigates to the `metrics_graph` route, then that route doesn't need to query the API for the list of metrics again.  Instead, it uses the values already stored in `state.api_data.metricIds` since these are global and persistent across routes.

However, let's say the user is at the `metrics_table` route and configures the table to show a particular type of bucket.  This is captured in `state.metrics_table.curBucketType`.  If the user visits the `metrics_graph` route now, then they will see a different bucket type based on `state.metrics_graph.curBucketType`.

### Global State Entries

Our various global entries in our state are as follows:

#### `api_data.buckets`
JS object of `modules/api_data/Bucket` objects, where each key is the Bucket's id.  A Bucket contains values for for our services/metrics for a particular time-range.  In short its how we encapsulate the time_series data we get back from a `api/metrics` call for a service.

#### `api_data.metrics`
JS object of `modules/api_data/Metric` objects, where each key is the Metric's id.  A Metric is how encapsulate the data we got back from `api/metrics`.

#### `api_data.metricIds`
All of the keys for our `api_data.metrics`.

#### `api_data.ranges`
JS object of `modules/api_data/Range` objects, where each key is the Range's id.  A Range contains a collection of Bucket Ids and represents all buckets relevant to a given time range.

#### `api_data.services`
JS object of `modules/api_data/Service` objects, where each key is the Service's id.  A Service is how encapsulate the data we got back from `api/projects`, `api/teams`, and `api/orgs`. Each Service object has a `children` property that contains the ids of related services.  I.e. an org many have some teams, which each may have some projects.

#### `api_data.orgIds`
All of the keys in our `api_data.services` that are of org services.

#### `api_data.fetchedBuckets`
Nested JS object that maps as `serviceId -> metricId -> bool`.  This indicates whether or not a particular service/metric pair has already returned its buckets.  This is used whenever we use the `fetchBuckets` action and provides a means of knowing when a particular service/metric pair has already returned is data prior to all buckets being returned.  This allows for things such as the NestedTable rendering the values of cells as buckets are received - rather than only rendering them once all values have been received.

#### `user_session.startTime`
The startTime that we use in all of our API calls for computing our ranges.  This is set when the user first visits the site and remains consistent for all routes until the user performs a refresh.

### Route State Entries

What is in our route entries varies between routes.  Take a look at its reducers for more information on what's in a particular route.  Particularly though, most routes have the following:

##### `route.loading`
Indicates whether or not the route is still loading.  The `metrics_table`route uses this to determine if certain components are locked out or displaying loading bars.

##### `route.error`
Indicates whether or not the route encountered an error.  The `metrics_table`route uses this to display an error message in the event of a failure.

### Bucket Data
If a route is showing bucket data, then the route will typically also have:

#### `route.curRangeType`
The current type of the range we're displaying data for.  I.e. is the range an entire month or year?

#### `route.curBucketType`
The current type of all of our buckets.  I.e. is each bucket within our range a week or a day?  The `metrics_table`route uses this populate our slider.  Each dot in the slider is a bucket relevant to our currently selected range.

#### `route.curBucketId`
The id of the current Bucket object that's being displayed.  The `metrics_table`route uses this to find the bucket whose values we use to populate the table.

## Development

#### Adding New Routes and Modules
Given that the App Structure is fractal, I am hoping that adding new routes and modules should be relatively straight-forward.  Follow the below guide for information on how to do so.

* [Adding a New Route Guide](docs/route-guide.md)

#### Documentation
As this application grows, I suspect that the number of reusable components and whatnot will increase.  I've been adding `README.md` files in component's directory, so its clear how a component can be used and what properties and whatnots it expects.  I ask that if you add new components then you continue to do so.

As for routes and other things, I've simply just been adding comments to the files themselves.

#### Developer Tools
* If you find yourself looking for a React IDE, then I'd suggest [Atom](https://atom.io/) with Facebook's [Nuclide](https://nuclide.io/) package.
* I would also highly recommend using the [Redux DevTools Chrome Extension](https://chrome.google.com/webstore/detail/redux-devtools/lmhkpmbekcpmknklioeibfkpmmfibljd) when debugging.

#### Styles
Both `.scss` and `.css` file extensions are supported out of the box. After being imported, styles will be processed with [PostCSS](https://github.com/postcss/postcss) for minification and auto-prefixing, and will be extracted to a `.css` file during production builds.

## Third Party Dependencies

Below is a list of the major 3rd party node packages that this project requires.  All of these are accounted for in the `packages.json` and installed when you run `yarn install`, so they're just listed here for reference.

|Package|Description|
|------------|---------|
|[enumify](http://www.2ality.com/2016/01/enumify.html)|Provides Enum support for JavaScript|
|[react-bootstrap](https://react-bootstrap.github.io/)|Integrates Bootstrap with React|
|[react-chartjs-2](https://github.com/gor181/react-chartjs-2)|Support for creating charts in React|
|[react-loading](https://github.com/cezary/react-loading)|Loading animation component for React|
|[react-router-bootstrap](https://github.com/react-bootstrap/react-router-bootstrap)|Integrates React Router with Bootstrap|
|[react-router-transition](https://github.com/maisano/react-router-transition)|Animation support for React Router|
|[rc-slider](https://github.com/react-component/slider)|Slider Bar component for React|