diff --git a/.bootstraprc b/.bootstraprc index 4623f419..df9bc9bb 100644 --- a/.bootstraprc +++ b/.bootstraprc @@ -5,7 +5,8 @@ "styles": { "mixins": true, "grid": true, - "forms": true + "forms": true, + "responsive-utilities":true }, "scripts": false diff --git a/app/assets/stylesheets/users/page.scss b/app/assets/stylesheets/users/page.scss new file mode 100644 index 00000000..461a885b --- /dev/null +++ b/app/assets/stylesheets/users/page.scss @@ -0,0 +1,3 @@ +.login-bottom-link { + margin-top: 12px; +} \ No newline at end of file diff --git a/app/controllers/users/sessions_controller.rb b/app/controllers/users/sessions_controller.rb index 2b2df43c..d9a4ad10 100644 --- a/app/controllers/users/sessions_controller.rb +++ b/app/controllers/users/sessions_controller.rb @@ -1,9 +1,17 @@ # License: AGPL-3.0-or-later WITH Web-Template-Output-Additional-Permission-3.0-or-later class Users::SessionsController < Devise::SessionsController + layout 'layouts/apified', only: :new + + + def new + @theme = 'minimal' + super + end def create - respond_to do |format| - format.html { super } + @theme = 'minimal' + + respond_to do |format| format.json { warden.authenticate!(:scope => resource_name, :recall => "#{controller_path}#new") render :status => 200, :json => { :status => "Success" } diff --git a/app/views/devise/sessions/new.html.erb b/app/views/devise/sessions/new.html.erb index 77a055ae..b38248a4 100755 --- a/app/views/devise/sessions/new.html.erb +++ b/app/views/devise/sessions/new.html.erb @@ -1,14 +1,12 @@ <%- # License: AGPL-3.0-or-later WITH Web-Template-Output-Additional-Permission-3.0-or-later -%> -<% content_for(:footer_hidden) {'hidden'} %> +<% content_for :title, t("login.header") %> +<% content_for :javascripts do %> + <%= IncludeAsset.js '/app/session_login_pagex.js' %> +<% end %> +<% content_for :stylesheets do %> + <%= stylesheet_link_tag 'users/page' %> +<% end %> -
-
-

Login

- <%= render 'users/email_login_form' %> -

- <%= link_to "Forgot your password?", new_password_path(resource_name)%> -

-

Don't have an account? Sign up.

-
-
+
+ diff --git a/app/views/onboard/index.html.erb b/app/views/onboard/index.html.erb index 12b40c93..d874b5c7 100644 --- a/app/views/onboard/index.html.erb +++ b/app/views/onboard/index.html.erb @@ -1,6 +1,6 @@ <% content_for :title, t("registration.get_started.header") %> <% content_for :javascripts do %> - <%= IncludeAsset.js 'app/registration_pagex.js' %> + <%= IncludeAsset.js '/app/registration_pagex.js' %> <% end %>
diff --git a/config/locales/en.yml b/config/locales/en.yml index 51c2adcc..554a819f 100644 --- a/config/locales/en.yml +++ b/config/locales/en.yml @@ -189,3 +189,11 @@ en: footer: terms_and_privacy: "Terms & Privacy" about: "About" + login: + header: "Login" + email: "Email" + password: "Password" + login: "Login" + logging_in: "Logging you in..." + forgot_password: "Forgot Password?" + get_started: "Get Started" diff --git a/config/routes.rb b/config/routes.rb index 4082b912..a9520158 100755 --- a/config/routes.rb +++ b/config/routes.rb @@ -195,7 +195,7 @@ Commitchange::Application.routes.draw do :confirmations => 'users/confirmations' } devise_scope :user do - match '/signin' => 'devise/sessions#new' + match '/sign_in' => 'users/sessions#new' match '/signup' => 'devise/registrations#new' post '/confirm' => 'users/confirmations#confirm' match '/users/is_confirmed' => 'users/confirmations#is_confirmed' diff --git a/javascripts/app/session_login_page.tsx b/javascripts/app/session_login_page.tsx new file mode 100644 index 00000000..0a0b8f63 --- /dev/null +++ b/javascripts/app/session_login_page.tsx @@ -0,0 +1,14 @@ +// License: LGPL-3.0-or-later +// require a root component here. This will be treated as the root of a webpack package +import Root from "../src/components/common/Root" +import SessionLoginPage from "../src/components/session_login_page/SessionLoginPage" + +import * as ReactDOM from 'react-dom' +import * as React from 'react' + +function LoadReactPage(element:HTMLElement) { + ReactDOM.render(, element) +} + + +(window as any).LoadReactPage = LoadReactPage \ No newline at end of file diff --git a/javascripts/src/components/registration_page/RegistrationWizard.tsx b/javascripts/src/components/registration_page/RegistrationWizard.tsx index 79cfc388..e7a7b8c6 100644 --- a/javascripts/src/components/registration_page/RegistrationWizard.tsx +++ b/javascripts/src/components/registration_page/RegistrationWizard.tsx @@ -198,7 +198,7 @@ export class InnerRegistrationWizard extends React.Component + buttonText="registration.wizard.save_and_finish" buttonTextOnProgress="registration.wizard.saving"/> } } diff --git a/javascripts/src/components/registration_page/UserInfoForm.tsx b/javascripts/src/components/registration_page/UserInfoForm.tsx index 05142d73..89140939 100644 --- a/javascripts/src/components/registration_page/UserInfoForm.tsx +++ b/javascripts/src/components/registration_page/UserInfoForm.tsx @@ -41,7 +41,7 @@ export interface UserInfoFormProps { form: Field buttonText:string - buttonTextInProgress?:string + buttonTextOnProgress?:string } @@ -63,7 +63,7 @@ class UserInfoForm extends React.Component ; } diff --git a/javascripts/src/components/registration_page/UserInfoPanel.tsx b/javascripts/src/components/registration_page/UserInfoPanel.tsx index ea81fc5d..4c793fcb 100644 --- a/javascripts/src/components/registration_page/UserInfoPanel.tsx +++ b/javascripts/src/components/registration_page/UserInfoPanel.tsx @@ -10,7 +10,7 @@ import UserInfoForm from "./UserInfoForm"; export interface UserInfoPanelProps extends WizardTabPanelProps { buttonText: string - buttonTextInProgress?:string + buttonTextOnProgress?:string } class UserInfoPanel extends React.Component { @@ -39,7 +39,7 @@ class UserInfoPanel extends React.Component - + ; } diff --git a/javascripts/src/components/session_login_page/SessionLoginForm.tsx b/javascripts/src/components/session_login_page/SessionLoginForm.tsx new file mode 100644 index 00000000..54af5557 --- /dev/null +++ b/javascripts/src/components/session_login_page/SessionLoginForm.tsx @@ -0,0 +1,138 @@ +// License: LGPL-3.0-or-later +import * as React from 'react'; +import { observer, inject} from 'mobx-react'; +import {InjectedIntlProps, injectIntl, FormattedMessage} from 'react-intl'; +import {Field, FieldDefinition, Form, initializationDefinition} from "../../../../types/mobx-react-form"; +import {Validations} from "../../lib/vjf_rules"; +import {WebLoginModel, WebUserSignInOut} from "../../lib/api/sign_in"; + +import {HoudiniForm, StaticFormToErrorAndBackConverter} from "../../lib/houdini_form"; +import {observable, action} from 'mobx' +import {ApiManager} from "../../lib/api_manager"; +import {BasicField} from "../common/fields"; +import ProgressableButton from "../common/ProgressableButton"; + +export interface SessionLoginFormProps +{ + + buttonText:string + buttonTextOnProgress:string + ApiManager?: ApiManager +} + +export const FieldDefinitions : Array = [ + { + name: 'email', + label: 'email', + type: 'text', + validators: [Validations.isFilled] + }, + { + name: 'password', + label: 'password', + type: 'password', + validators: [Validations.isFilled] + } +] + +export class SessionPageForm extends HoudiniForm { + converter: StaticFormToErrorAndBackConverter + + constructor(definition: initializationDefinition, options?: any) { + super(definition, options) + this.converter = new StaticFormToErrorAndBackConverter(this.inputToForm) + } + + signinApi: WebUserSignInOut + + options() { + return { + validateOnInit: true, + validateOnChange: true, + retrieveOnlyDirtyValues: true, + retrieveOnlyEnabledFields: true + } + } + + inputToForm = { + 'email': 'email', + 'password': 'password' + } + + hooks() { + return { + onSuccess: async (f:SessionPageForm) => { + let input = this.converter.convertFormToObject(f) + + try{ + let r = await this.signinApi.postLogin(input) + window.location.reload() + } + catch(e){ + if (e.error) { + f.invalidate(e.error) + } + else { + f.invalidate(e) + } + } + } + } + } +} + + +class InnerSessionLoginForm extends React.Component { + constructor(props: SessionLoginFormProps & InjectedIntlProps) { + super(props) + this.createForm(); + } + + @action.bound + createForm() { + this.form = new SessionPageForm({fields: FieldDefinitions}) + } + + @observable form: SessionPageForm + + render() { + + if(!this.form.signinApi){ + this.form.signinApi = this.props.ApiManager.get(WebUserSignInOut) + } + let label: {[props:string]: string} = { + 'email': "login.email", + "password": 'login.password', + } + + for (let key in label){ + this.form.$(key).set('label', this.props.intl.formatMessage({id: label[key]})) + } + + let errorDiv = !this.form.isValid ?
{(this.form as any).error}
: '' + + return
+ + + {errorDiv} +
+ +
+
+
+
+
+ ; + } +} + +export default injectIntl( + inject('ApiManager') + (observer( InnerSessionLoginForm) + ) +) + + + diff --git a/javascripts/src/components/session_login_page/SessionLoginPage.tsx b/javascripts/src/components/session_login_page/SessionLoginPage.tsx new file mode 100644 index 00000000..492d159d --- /dev/null +++ b/javascripts/src/components/session_login_page/SessionLoginPage.tsx @@ -0,0 +1,25 @@ +// License: LGPL-3.0-or-later +import * as React from 'react'; +import { observer } from 'mobx-react'; +import {InjectedIntlProps, injectIntl, InjectedIntl, FormattedMessage} from 'react-intl'; +import SessionLoginForm from "./SessionLoginForm"; + +export interface SessionLoginPageProps +{ + +} + +class SessionLoginPage extends React.Component { + render() { + return
+

+ +
+
; + } +} + +export default injectIntl(observer(SessionLoginPage)) + + + diff --git a/javascripts/src/lib/api/sign_in.ts b/javascripts/src/lib/api/sign_in.ts index b524170c..55dce752 100644 --- a/javascripts/src/lib/api/sign_in.ts +++ b/javascripts/src/lib/api/sign_in.ts @@ -72,7 +72,7 @@ export class WebUserSignInOut { (xhr: JQueryXHR, textStatus: string, errorThrown: string) => { - dfd.reject(errorThrown) + dfd.reject(xhr.responseJSON) } ); @@ -80,7 +80,7 @@ export class WebUserSignInOut { } } -interface WebLoginModel { +export interface WebLoginModel { email:string password:string } \ No newline at end of file diff --git a/types/mobx-react-form/index.d.ts b/types/mobx-react-form/index.d.ts index 8696dacb..afb92ddf 100644 --- a/types/mobx-react-form/index.d.ts +++ b/types/mobx-react-form/index.d.ts @@ -306,6 +306,9 @@ export class Form implements Base { readonly submitting: boolean; protected validator :any + + readonly isValid :boolean; + }