diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index 8ed751a..fc9baee 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -19,3 +19,5 @@ A **Task** is something that is not complex enough to be defined as a **Feature* The title of a an issue that is expected to be treated as a **BUG** must be prefixed with **BUG:**, i.e., **BUG: Wrong new notifications count**. Submit your pull requests to the corresponding branch according to the branching model mentioned at the beginning of this section. + +Last but not least, [stop using `git pull`](https://adamcod.es/2014/12/10/git-pull-correct-workflow.html) diff --git a/README.md b/README.md index 72ec59a..f4d89be 100644 --- a/README.md +++ b/README.md @@ -1,4 +1,4 @@ -# phoenix-webpack-relay-react (pwr2) +# Phoenix, Webpack, React and Relay (pwr2) Docker image based on Ubuntu providing a base setup for a Phoenix+Webpack+Relay+React project, with some sugar and conventions to develop and build your own web applications. @@ -6,6 +6,22 @@ _We will improve docs and code including test coverage in the next releases_ **NOTICE:** The default branch for this repo is **develop**. Check the [README](https://github.com/iporaitech/pwr2-docker/blob/master/README.md) on master to see what's in the last release. +## What is this for? + +You can use this to start your own Elixir/Phoenix based web application with React+Relay - styled with Material Design, in the front-end all bundled with Webpack. + +So far we've implemented the following: + +* A GraphQL endpoint implemented in Elixir with Absinthe. +* Authentication using JWTs (JSON Web Tokens) via GraphQL (LoginMutation & LogoutMutation). +* Hardcoded Role based Authorization. +* StarWars GraphQL example. +* GraphiQL console. +* Some interesting React/Relay components in the client(browser), including a router. +* CSS Modules _integration_ with Material Design Lite. +* Testing framework for the backend (Elixir/Phoenix). + + ## Requirements To run this software you need to install [Docker](https://www.docker.com/) on your system. It is very simple and there are a lot of guides and tutorials out there on the Internet. @@ -97,7 +113,7 @@ Once the containers are up and running you can copy the source code of the base Once all setup and with the app running and assuming your `HTTP_PORT` is 4000, you can: -0. Login with credentials available in [priv/repo/seeds.exs](priv/repo/seeds.exs). Logout is also available **BUT DISPLAYING AN ERROR when trying lo Login with wrong credentials is not implemented yet**. +0. Login with credentials available in [priv/repo/seeds.exs](priv/repo/seeds.exs). Logout is also available. 1. Visit http://localhost:4000/admin/graphiql to access a [GraphiQL](https://github.com/graphql/graphiql) IDE. 2. Visit http://localhost:4000/admin/star-wars to experiment with our implementation of the [Relay Star Wars example](https://github.com/relayjs/relay-examples/tree/master/star-wars). The _[database](./web/graphql/star_wars_db.ex)_ for this example is implemented as an [Elixir.Agent](http://elixir-lang.org/docs/stable/elixir/Agent.html) 3. You can also use something like Google Chrome's Advanced Rest Client(ARC) or any other JSON API client and (with the corresponding Authorization header) send queries to http://localhost:4000/graphql like: diff --git a/mix.exs b/mix.exs index deea994..0658a03 100644 --- a/mix.exs +++ b/mix.exs @@ -3,7 +3,7 @@ defmodule Webapp.Mixfile do def project do [app: :webapp, - version: "0.3.0", + version: "0.3.2", elixir: "~> 1.3", elixirc_paths: elixirc_paths(Mix.env), compilers: [:phoenix, :gettext] ++ Mix.compilers, diff --git a/package.json b/package.json index 9b00ba7..35c521d 100644 --- a/package.json +++ b/package.json @@ -2,7 +2,7 @@ "name": "pwr2-docker-ui", "description": "Javascript dependencies for pwr2-docker, a Docker image based on Ubuntu providing a base setup for a Phoenix+Webpack+Relay+React project, with some sugar and conventions to develop and build your own web applications.", "main": "webpack.config.js", - "version": "0.3.0", + "version": "0.3.2", "contributors": [ { "name": "Iporaitech and others", @@ -33,6 +33,7 @@ }, "dependencies": { "babel-runtime": "^6.11.6", + "classnames": "^2.2.5", "es6-promise": "^3.2.1", "graphiql": "^0.7.3", "graphql": "^0.6.2", @@ -48,5 +49,5 @@ "react-router-relay": "https://github.com/iporaitech/react-router-relay/tarball/build", "sync-request": "^3.0.1" }, - "license" : "MIT" + "license": "MIT" } diff --git a/web/graphql/types/auth_mutations.ex b/web/graphql/types/auth_mutations.ex index cf3c003..36f3179 100644 --- a/web/graphql/types/auth_mutations.ex +++ b/web/graphql/types/auth_mutations.ex @@ -3,8 +3,11 @@ defmodule Webapp.GraphQL.Types.AuthMutations do use Absinthe.Relay.Schema.Notation import Comeonin.Bcrypt, only: [checkpw: 2, dummy_checkpw: 0] - #TODO: Create resolve functions for these mutations in their own module and test them. - # It will be easier to test the resolvers in isolation than embedded in the muations. + #TODO: + # 1. Create resolve functions for these mutations in their own module and test them. It + # will be easier to test the resolvers in isolation than embedded in the muations. + # 2. Refactor to use Scalar types for email and password. + # See http://graphql.org/learn/schema/#scalar-types object :auth_mutations do payload field :login do diff --git a/web/static/js/layout/index.js b/web/static/js/layout/index.js index 40beeb7..7ceb94a 100644 --- a/web/static/js/layout/index.js +++ b/web/static/js/layout/index.js @@ -1,7 +1,7 @@ // file: layout/index.js import React from 'react'; import { Link } from 'react-router'; -import LogoutLink from 'lib/LogoutLink'; +import LogoutLink from 'shared/LogoutLink'; import mdlUpgrade from 'lib/mdlUpgrade'; import styles from 'material-design-lite/material.css'; diff --git a/web/static/js/login/index.js b/web/static/js/login/index.js index 51779fc..4cf1a8b 100644 --- a/web/static/js/login/index.js +++ b/web/static/js/login/index.js @@ -2,23 +2,29 @@ import React from 'react'; import Relay from 'react-relay'; import mdlUpgrade from 'lib/mdlUpgrade'; +import Loading from 'shared/loading'; import material from 'material-design-lite/material.css'; +import classNames from 'classnames/bind'; import styles from './styles.css'; import { withRouter } from 'react-router'; import LoginMutation from './mutation'; import Auth from 'lib/auth'; +const cx = classNames.bind(styles); + class Login extends React.Component { constructor(props) { super(props); this.state = { - error: false + hasError: false, + isLoading: false } } handleSubmit(event) { event.preventDefault(); + this.setState({isLoading: true}); this.props.relay.commitUpdate( new LoginMutation({ @@ -34,40 +40,55 @@ class Login extends React.Component { } else { router.replace('/') } + }, + onFailure: transaction => { + this.setState({hasError: true}); + this.setState({isLoading: false}); } } ); } render() { + // We use classNames for CSS that depends on MDL javascript + const inputClassName = cx( + "mdl-js-textfield", + 'mdl-textfield', + 'mdl-textfield--floating-label', + {"is-invalid": this.state.hasError} + ); + const { isLoading } = this.state; + return (