Commit 313c616d authored by arturo's avatar arturo

>>> continue

parent c4060538
...@@ -9699,6 +9699,71 @@ input[type=range]:-moz-focusring { ...@@ -9699,6 +9699,71 @@ input[type=range]:-moz-focusring {
gap: 28px; gap: 28px;
} }
.login-modal-form__title {
position: relative;
background-color: #dee2e6;
padding: 0.75rem 1.25rem;
text-align: center;
margin-top: -1rem;
margin-left: -1rem;
margin-right: -1rem;
}
.login-modal-form__title__return {
top: 50%;
left: 50%;
transform: translateX(-50%) translateY(-50%);
position: absolute;
left: 28px;
}
.login-modal-form__title__text {
font-family: SFMono-Regular, Menlo, Monaco, Consolas, "Liberation Mono", "Courier New", monospace;
font-size: 20px;
font-weight: bold;
}
.login-modal-form__separator {
padding: 0.75rem calc(2* 1.25rem);
position: relative;
}
.login-modal-form__separator__text {
top: 50%;
left: 50%;
transform: translateX(-50%) translateY(-50%);
position: absolute;
display: inline-block;
padding-left: 32px;
padding-right: 32px;
font-family: "Comfortaa";
font-size: 24px;
background-color: #fff;
}
.login-modal-form__request-access {
margin-top: calc(3 * 0.75rem);
margin-left: auto;
margin-right: auto;
margin-bottom: 0.75rem;
padding: 0.5rem 1rem;
width: 200px;
display: block;
}
.login-modal-form__log-in {
padding: 0.5rem 1rem;
width: 200px;
margin-bottom: calc(2 * 0.75rem);
margin-left: auto;
margin-right: auto;
margin-top: 0.75rem;
display: block;
}
.login-modal-form__form {
margin: 0 auto;
width: 520px;
}
.login-modal-form__error {
margin-bottom: 24px;
color: #FF304F;
text-align: center;
}
.maintree { .maintree {
position: relative; position: relative;
animation: 15ms fade-in; animation: 15ms fade-in;
......
...@@ -9648,6 +9648,70 @@ input[type=range]:-moz-focusring { ...@@ -9648,6 +9648,70 @@ input[type=range]:-moz-focusring {
gap: 28px; gap: 28px;
} }
.login-modal-form__title {
position: relative;
background-color: #dee2e6;
padding: 0.75rem 1.25rem;
text-align: center;
margin-top: -1rem;
margin-left: -1rem;
margin-right: -1rem;
}
.login-modal-form__title__return {
top: 50%;
left: 50%;
transform: translateX(-50%) translateY(-50%);
position: absolute;
left: 28px;
}
.login-modal-form__title__text {
font-family: SFMono-Regular, Menlo, Monaco, Consolas, "Liberation Mono", "Courier New", monospace;
font-size: 20px;
font-weight: bold;
}
.login-modal-form__separator {
padding: 0.75rem calc(2* 1.25rem);
position: relative;
}
.login-modal-form__separator__text {
top: 50%;
left: 50%;
transform: translateX(-50%) translateY(-50%);
position: absolute;
display: inline-block;
padding-left: 32px;
padding-right: 32px;
font-size: 24px;
background-color: #fff;
}
.login-modal-form__request-access {
margin-top: calc(3 * 0.75rem);
margin-left: auto;
margin-right: auto;
margin-bottom: 0.75rem;
padding: 0.5rem 1rem;
width: 200px;
display: block;
}
.login-modal-form__log-in {
padding: 0.5rem 1rem;
width: 200px;
margin-bottom: calc(2 * 0.75rem);
margin-left: auto;
margin-right: auto;
margin-top: 0.75rem;
display: block;
}
.login-modal-form__form {
margin: 0 auto;
width: 520px;
}
.login-modal-form__error {
margin-bottom: 24px;
color: #dc3545;
text-align: center;
}
.maintree { .maintree {
position: relative; position: relative;
animation: 15ms fade-in; animation: 15ms fade-in;
......
...@@ -9408,6 +9408,71 @@ input[type=range]:-moz-focusring { ...@@ -9408,6 +9408,71 @@ input[type=range]:-moz-focusring {
gap: 28px; gap: 28px;
} }
.login-modal-form__title {
position: relative;
background-color: #dee2e6;
padding: 0.75rem 1.25rem;
text-align: center;
margin-top: -1rem;
margin-left: -1rem;
margin-right: -1rem;
}
.login-modal-form__title__return {
top: 50%;
left: 50%;
transform: translateX(-50%) translateY(-50%);
position: absolute;
left: 28px;
}
.login-modal-form__title__text {
font-family: SFMono-Regular, Menlo, Monaco, Consolas, "Liberation Mono", "Courier New", monospace;
font-size: 20px;
font-weight: bold;
}
.login-modal-form__separator {
padding: 0.75rem calc(2* 1.25rem);
position: relative;
}
.login-modal-form__separator__text {
top: 50%;
left: 50%;
transform: translateX(-50%) translateY(-50%);
position: absolute;
display: inline-block;
padding-left: 32px;
padding-right: 32px;
font-family: "Oswald";
font-size: 24px;
background-color: #fff;
}
.login-modal-form__request-access {
margin-top: calc(3 * 0.75rem);
margin-left: auto;
margin-right: auto;
margin-bottom: 0.75rem;
padding: 0.5rem 1rem;
width: 200px;
display: block;
}
.login-modal-form__log-in {
padding: 0.5rem 1rem;
width: 200px;
margin-bottom: calc(2 * 0.75rem);
margin-left: auto;
margin-right: auto;
margin-top: 0.75rem;
display: block;
}
.login-modal-form__form {
margin: 0 auto;
width: 520px;
}
.login-modal-form__error {
margin-bottom: 24px;
color: #cc330d;
text-align: center;
}
.maintree { .maintree {
position: relative; position: relative;
animation: 15ms fade-in; animation: 15ms fade-in;
......
...@@ -9656,6 +9656,71 @@ input[type=range]:-moz-focusring { ...@@ -9656,6 +9656,71 @@ input[type=range]:-moz-focusring {
gap: 28px; gap: 28px;
} }
.login-modal-form__title {
position: relative;
background-color: #dee2e6;
padding: 0.75rem 1.25rem;
text-align: center;
margin-top: -1rem;
margin-left: -1rem;
margin-right: -1rem;
}
.login-modal-form__title__return {
top: 50%;
left: 50%;
transform: translateX(-50%) translateY(-50%);
position: absolute;
left: 28px;
}
.login-modal-form__title__text {
font-family: SFMono-Regular, Menlo, Monaco, Consolas, "Liberation Mono", "Courier New", monospace;
font-size: 20px;
font-weight: bold;
}
.login-modal-form__separator {
padding: 0.75rem calc(2* 1.25rem);
position: relative;
}
.login-modal-form__separator__text {
top: 50%;
left: 50%;
transform: translateX(-50%) translateY(-50%);
position: absolute;
display: inline-block;
padding-left: 32px;
padding-right: 32px;
font-family: "Crete Round";
font-size: 24px;
background-color: #fff;
}
.login-modal-form__request-access {
margin-top: calc(3 * 0.75rem);
margin-left: auto;
margin-right: auto;
margin-bottom: 0.75rem;
padding: 0.5rem 1rem;
width: 200px;
display: block;
}
.login-modal-form__log-in {
padding: 0.5rem 1rem;
width: 200px;
margin-bottom: calc(2 * 0.75rem);
margin-left: auto;
margin-right: auto;
margin-top: 0.75rem;
display: block;
}
.login-modal-form__form {
margin: 0 auto;
width: 520px;
}
.login-modal-form__error {
margin-bottom: 24px;
color: #FF4057;
text-align: center;
}
.maintree { .maintree {
position: relative; position: relative;
animation: 15ms fade-in; animation: 15ms fade-in;
......
...@@ -9657,6 +9657,71 @@ input[type=range]:-moz-focusring { ...@@ -9657,6 +9657,71 @@ input[type=range]:-moz-focusring {
gap: 28px; gap: 28px;
} }
.login-modal-form__title {
position: relative;
background-color: #dee2e6;
padding: 0.75rem 1.25rem;
text-align: center;
margin-top: -1rem;
margin-left: -1rem;
margin-right: -1rem;
}
.login-modal-form__title__return {
top: 50%;
left: 50%;
transform: translateX(-50%) translateY(-50%);
position: absolute;
left: 28px;
}
.login-modal-form__title__text {
font-family: SFMono-Regular, Menlo, Monaco, Consolas, "Liberation Mono", "Courier New", monospace;
font-size: 20px;
font-weight: bold;
}
.login-modal-form__separator {
padding: 0.75rem calc(2* 1.25rem);
position: relative;
}
.login-modal-form__separator__text {
top: 50%;
left: 50%;
transform: translateX(-50%) translateY(-50%);
position: absolute;
display: inline-block;
padding-left: 32px;
padding-right: 32px;
font-family: "Open Sans";
font-size: 24px;
background-color: #fff;
}
.login-modal-form__request-access {
margin-top: calc(3 * 0.75rem);
margin-left: auto;
margin-right: auto;
margin-bottom: 0.75rem;
padding: 0.5rem 1rem;
width: 200px;
display: block;
}
.login-modal-form__log-in {
padding: 0.5rem 1rem;
width: 200px;
margin-bottom: calc(2 * 0.75rem);
margin-left: auto;
margin-right: auto;
margin-top: 0.75rem;
display: block;
}
.login-modal-form__form {
margin: 0 auto;
width: 520px;
}
.login-modal-form__error {
margin-bottom: 24px;
color: #434343;
text-align: center;
}
.maintree { .maintree {
position: relative; position: relative;
animation: 15ms fade-in; animation: 15ms fade-in;
......
module Gargantext.Components.Forms where
import Record as Record
import Reactix as R
import Reactix.DOM.HTML as H
import Gargantext.Utils.Reactix as R2
clearfix :: R.Element
clearfix = H.div { className: "clearfix" } []
formGroup :: Array R.Element -> R.Element
formGroup = H.div { className: "form-group" }
center :: Array R.Element -> R.Element
center = H.div { className: "center" }
card :: Array R.Element -> R.Element
card = H.div { className: "card" }
cardBlock :: Array R.Element -> R.Element
cardBlock = H.div { className: "card-block" }
cardGroup :: Array R.Element -> R.Element
cardGroup = H.div { className: "card-group" }
...@@ -17,7 +17,6 @@ module Gargantext.Components.Login ...@@ -17,7 +17,6 @@ module Gargantext.Components.Login
import Gargantext.Prelude import Gargantext.Prelude
import DOM.Simple.Event as DE
import Data.Array (head) import Data.Array (head)
import Data.Foldable (intercalate) import Data.Foldable (intercalate)
import Data.Maybe (Maybe(..), fromMaybe) import Data.Maybe (Maybe(..), fromMaybe)
...@@ -27,9 +26,9 @@ import Effect (Effect) ...@@ -27,9 +26,9 @@ import Effect (Effect)
import Effect.Aff (Milliseconds(..), delay, launchAff_) import Effect.Aff (Milliseconds(..), delay, launchAff_)
import Effect.Class (liftEffect) import Effect.Class (liftEffect)
import Gargantext.Components.Bootstrap as B import Gargantext.Components.Bootstrap as B
import Gargantext.Components.Bootstrap.Types (ComponentStatus(..), Elevation(..), ModalSizing(..), Position(..), TooltipPosition(..)) import Gargantext.Components.Bootstrap.Types (ComponentStatus(..), Elevation(..), ModalSizing(..), Position(..), TooltipPosition(..), Variant(..))
import Gargantext.Components.Login.ForgotPassword (forgotPassword) import Gargantext.Components.Login.ForgotPassword (forgotPassword)
import Gargantext.Components.Login.Form (form) import Gargantext.Components.Login.Form as Form
import Gargantext.Components.Login.Types (FormType(..)) import Gargantext.Components.Login.Types (FormType(..))
import Gargantext.Components.NgramsTable.Loader as NTL import Gargantext.Components.NgramsTable.Loader as NTL
import Gargantext.Ends (Backend(..)) import Gargantext.Ends (Backend(..))
...@@ -40,7 +39,7 @@ import Gargantext.Utils (nbsp, (?)) ...@@ -40,7 +39,7 @@ import Gargantext.Utils (nbsp, (?))
import Gargantext.Utils.Reactix as R2 import Gargantext.Utils.Reactix as R2
import Reactix as R import Reactix as R
import Reactix.DOM.HTML as H import Reactix.DOM.HTML as H
import Reactix.SyntheticEvent as RE import Record as Record
import Toestand as T import Toestand as T
here :: R2.Here here :: R2.Here
...@@ -88,7 +87,7 @@ loginContainerCpt = here.component "container" cpt where ...@@ -88,7 +87,7 @@ loginContainerCpt = here.component "container" cpt where
-- | States -- | States
-- | -- |
mBackend <- R2.useLive' props.backend mBackend <- R2.useLive' props.backend
formType <- T.useBox Login formType <- T.useBox Manager
formType' <- T.useLive T.unequal formType formType' <- T.useLive T.unequal formType
-- | Render -- | Render
...@@ -99,10 +98,14 @@ loginContainerCpt = here.component "container" cpt where ...@@ -99,10 +98,14 @@ loginContainerCpt = here.component "container" cpt where
[] []
[ [
case mBackend of case mBackend of
Nothing -> chooser props Nothing ->
chooser $
{ formType
} `Record.merge` props
Just backend -> case formType' of Just backend -> case formType' of
Login -> Login ->
form Form.component
{ backend { backend
, formType , formType
, sessions , sessions
...@@ -113,13 +116,27 @@ loginContainerCpt = here.component "container" cpt where ...@@ -113,13 +116,27 @@ loginContainerCpt = here.component "container" cpt where
{ backend { backend
, sessions , sessions
} }
Manager ->
chooser $
{ formType
} `Record.merge` props
] ]
chooser :: R2.Leaf Props type ChooserProps =
( formType :: T.Box FormType
| Props
)
chooser :: R2.Leaf ChooserProps
chooser = R2.leaf chooserCpt chooser = R2.leaf chooserCpt
chooserCpt :: R.Component Props chooserCpt :: R.Component ChooserProps
chooserCpt = here.component "chooser" cpt where chooserCpt = here.component "chooser" cpt where
cpt { backend, backends, sessions } _ = do cpt { backend
, backends
, sessions
, formType
} _ = do
-- | States -- | States
-- | -- |
sessions' <- T.useLive T.unequal sessions sessions' <- T.useLive T.unequal sessions
...@@ -135,15 +152,22 @@ chooserCpt = here.component "chooser" cpt where ...@@ -135,15 +152,22 @@ chooserCpt = here.component "chooser" cpt where
[ [
H.h6 H.h6
{} {}
[ [ H.text "Existing places" ]
H.text "Existing places"
,
H.text $ nbsp 1
, ,
B.span' B.tooltipContainer
{ className: "font-weight-normal" } { position: TooltipPosition Top
"(click to login)" , variant: Info
] , tooltipSlot:
B.span_ "Available soon"
, defaultSlot:
B.formInput
{ status: Idled
, placeholder: "Search for your institute"
, value: (mempty :: String)
, callback: const R.nothing
, className: "mb-1"
}
}
, ,
H.table H.table
{ className : "table" } { className : "table" }
...@@ -160,16 +184,16 @@ chooserCpt = here.component "chooser" cpt where ...@@ -160,16 +184,16 @@ chooserCpt = here.component "chooser" cpt where
] ]
, ,
H.tbody H.tbody
{} {} $
(map (renderBackend backend) backends) backends <#>
]
, renderBackend <<<
H.input { backendBox: backend
{ className: "form-control" , formTypeBox: formType
, type:"text" , backend: _
, placeholder: "Search for your institute"
} }
] ]
]
-- Shown in the chooser -- Shown in the chooser
activeConnections :: T.Box Sessions -> Sessions -> Array R.Element activeConnections :: T.Box Sessions -> Sessions -> Array R.Element
...@@ -298,9 +322,32 @@ renderSessionCpt = here.component "renderSession" cpt where ...@@ -298,9 +322,32 @@ renderSessionCpt = here.component "renderSession" cpt where
] ]
] ]
type RenderBackendProps =
( backendBox :: T.Box (Maybe Backend)
, formTypeBox :: T.Box FormType
, backend :: Backend
)
renderBackend :: R2.Leaf RenderBackendProps
renderBackend = R2.leaf renderBackendCpt
renderBackendCpt :: R.Component RenderBackendProps
renderBackendCpt = here.component "renderBackend" cpt where
cpt { backendBox
, backend: backend@(Backend { name, baseUrl, backendType })
, formTypeBox
} _ = do
-- | Behaviors
-- |
let
click :: Unit -> Effect Unit
click _ = do
T.write_ (Just backend) backendBox
T.write_ (Login) formTypeBox
-- | Render
-- |
pure $
renderBackend :: forall b. T.Write b (Maybe Backend) => b -> Backend -> R.Element
renderBackend cursor backend@(Backend {name, baseUrl, backendType}) =
H.tr {} H.tr {}
[ [
H.td H.td
...@@ -338,12 +385,8 @@ renderBackend cursor backend@(Backend {name, baseUrl, backendType}) = ...@@ -338,12 +385,8 @@ renderBackend cursor backend@(Backend {name, baseUrl, backendType}) =
] ]
] ]
] ]
where
click :: RE.SyntheticEvent DE.Event -> Effect Unit
click e = do
RE.preventDefault e
T.write_ (Just backend) cursor
backendLabel :: String -> String backendLabel :: String -> String
backendLabel = backendLabel =
......
...@@ -8,7 +8,6 @@ import Effect (Effect) ...@@ -8,7 +8,6 @@ import Effect (Effect)
import Effect.Aff (launchAff_) import Effect.Aff (launchAff_)
import Effect.Class (liftEffect) import Effect.Class (liftEffect)
import Formula as F import Formula as F
import Gargantext.Components.Forms (formGroup)
import Gargantext.Ends (Backend) import Gargantext.Ends (Backend)
import Gargantext.Sessions (Sessions, postForgotPasswordRequest) import Gargantext.Sessions (Sessions, postForgotPasswordRequest)
import Gargantext.Utils.Reactix as R2 import Gargantext.Utils.Reactix as R2
...@@ -39,7 +38,7 @@ forgotPasswordCpt = here.component "forgotPassword" cpt where ...@@ -39,7 +38,7 @@ forgotPasswordCpt = here.component "forgotPassword" cpt where
[ H.form { className: "text-center col-md-12" } [ H.form { className: "text-center col-md-12" }
[ H.h4 {} [ H.text "Forgot password" ] [ H.h4 {} [ H.text "Forgot password" ]
, messageDisplay { message } , messageDisplay { message }
, formGroup , H.div { className: "form-group" }
[ emailInput { email, disabled} ] [ emailInput { email, disabled} ]
, submitButton { backend, email, sessions, message, disabled } , submitButton { backend, email, sessions, message, disabled }
] ]
......
module Gargantext.Components.Login.Form where module Gargantext.Components.Login.Form
( component
) where
import Gargantext.Prelude
import Prelude (Unit, bind, discard, not, notEq, pure, show, unit, ($), (&&), (*>), (<>))
import Data.Either (Either(..)) import Data.Either (Either(..))
import DOM.Simple.Event as DE import Data.Foldable (foldl, intercalate)
import Data.Maybe (Maybe(..))
import Data.String as String
import Data.Tuple.Nested ((/\))
import Effect (Effect) import Effect (Effect)
import Effect.Aff (launchAff_) import Effect.Aff (Aff, launchAff_)
import Effect.Class (liftEffect) import Effect.Class (liftEffect)
import Formula as F import Gargantext.Components.Bootstrap as B
import Reactix as R import Gargantext.Components.Bootstrap.Types (ButtonVariant(..), ComponentStatus(..), Elevation(..), Sizing(..), Variant(..))
import Reactix.SyntheticEvent as E
import Reactix.DOM.HTML as H
import Toestand as T
import Toestand (useFocusedFields)
import Data.String as String
import Gargantext.Components.Forms (clearfix, formGroup)
import Gargantext.Components.Login.Types (AuthRequest(..), FormType(..)) import Gargantext.Components.Login.Types (AuthRequest(..), FormType(..))
import Gargantext.Ends (Backend) import Gargantext.Ends (Backend)
import Gargantext.Hooks.FormValidation (VForm, useFormValidation)
import Gargantext.Hooks.FormValidation.Unboxed as FV
import Gargantext.Hooks.StateRecord (useStateRecord)
import Gargantext.Sessions (Session, Sessions, postAuthRequest)
import Gargantext.Sessions as Sessions import Gargantext.Sessions as Sessions
import Gargantext.Sessions (Sessions, postAuthRequest) import Gargantext.Utils ((?))
import Gargantext.Utils (csrfMiddlewareToken)
import Gargantext.Utils.Reactix as R2 import Gargantext.Utils.Reactix as R2
import Reactix as R
import Reactix.DOM.HTML as H
import Record as Record
import Toestand as T
here :: R2.Here here :: R2.Here
here = R2.here "Gargantext.Components.Login.Form" here = R2.here "Gargantext.Components.Login.Form"
type Form = type Props =
{ error :: String ( backend :: Backend
, username :: String , formType :: T.Box FormType
, password :: String , sessions :: T.Box Sessions
, agreed :: Boolean , visible :: T.Box Boolean
} )
emptyForm :: Form component :: R2.Leaf Props
emptyForm = { error: "", username: "", password: "", agreed: false } component = R2.leaf componentCpt
componentCpt :: R.Component Props
componentCpt = here.component "main" cpt where
cpt { backend
, formType
, sessions
, visible
} _ = do
-- | States
-- |
error' /\ error
<- R2.useBox' (Nothing :: Maybe String)
type Boxes = onPending' /\ onPending
{ error :: T.Box String <- R2.useBox' false
, username :: T.Box String
, password :: T.Box String
, agreed :: T.Box Boolean }
formBoxes :: T.Box Form -> R.Hooks Boxes -- | Hooks
formBoxes box = useFocusedFields box {} -- |
{ state, bindStateKey } <- useStateRecord (defaultData :: FormData)
fv <- useFormValidation
type Props s v = -- | Behaviors
( backend :: Backend -- |
, formType :: T.Box FormType let
, sessions :: s onReturnClick :: Unit -> Effect Unit
, visible :: v onReturnClick _ = T.write_ (Manager) formType
)
form :: forall s v. T.ReadWrite s Sessions => T.ReadWrite v Boolean onPasswordForgottenClick :: Unit -> Effect Unit
=> Record (Props s v) -> R.Element onPasswordForgottenClick _ = T.write_ (ForgotPassword) formType
form props = R.createElement formCpt props []
formCpt :: forall s v. T.ReadWrite s Sessions => T.ReadWrite v Boolean
=> R.Component (Props s v)
formCpt = here.component "form" cpt where
cpt { backend, formType, sessions, visible } _ = do
cell <- T.useBox emptyForm
cursors <- useFocusedFields cell {}
pure $ R2.row
[ H.form { className: "col-md-12" }
[ formLoginLink backend
, requestAccessLink
, csrfTokenInput
, formGroup
[ H.p {} [ F.viewText { text: cursors.error } ]
, usernameInput cursors.username ]
, formGroup
[ passwordInput cursors.password
, clearfix ]
, termsCheckbox cursors.agreed
, submitButton { backend, formType, sessions, visible, cell }
, forgotPassword { formType }
]]
-- might be wrong, all we care about is preventDefault
type ChangeEvent = R.SyntheticEvent DE.MouseEvent
formLoginLink :: Backend -> R.Element
formLoginLink backend =
H.h4 { className: "text-center" } {-className: "text-muted"-}
[ H.text $ "Login to garg://" <> show backend ]
type SubmitButtonProps s v = ( cell :: T.Box Form | Props s v )
submitButton :: forall s v. T.ReadWrite s Sessions => T.Write v Boolean
=> R2.Leaf (SubmitButtonProps s v)
submitButton = R2.leafComponent submitButtonCpt
submitButtonCpt :: forall s v. T.ReadWrite s Sessions => T.Write v Boolean
=> R.Component (SubmitButtonProps s v)
submitButtonCpt = here.component "submitButton" cpt where
cpt { backend, formType, sessions, visible, cell } _ = do
{ agreed, username, password } <- T.useLive T.unequal cell
let isValid = agreed && (username `notEq` "") && (password `notEq` "")
pure $ H.div { className: "text-center" }
[ loginSubmit isValid $ submitForm { backend, formType, sessions, visible } cell ]
-- Attempts to submit the form
submitForm :: forall s v. T.ReadWrite s Sessions => T.Write v Boolean
=> Record (Props s v) -> T.Box Form -> ChangeEvent -> Effect Unit
submitForm { backend, sessions, visible } cell e = do
E.preventDefault e
state <- T.read cell
launchAff_ $ do
res <- postAuthRequest backend (req state)
_ <- case res of
Left message -> liftEffect $ T.write (state { error = message }) cell
Right sess ->
liftEffect $
Sessions.change (Sessions.Login sess) sessions
*> T.write false visible
*> T.write (state { error = "" }) cell
pure unit
where
-- User usually copy space before or after the username and password
req { username, password } = AuthRequest {username:cleanString username, password:cleanString password }
-- req { username, password } = AuthRequest {username, password }
cleanString :: String -> String onSubmit :: Unit -> Effect Unit
cleanString str = String.replace (String.Pattern " ") onSubmit _ = do
(String.Replacement "") str
result <- fv.try (\_ -> formValidation state)
case result of
Left err -> here.log3 "validation error" state err
csrfTokenInput :: R.Element -- TODO hard-coded CSRF token Right _ -> do
csrfTokenInput = H.input { type: "hidden", name, value } where T.write_ true onPending
name = "csrfmiddlewaretoken" launchAff_
value = csrfMiddlewareToken $ signin backend state
>>= case _ of
termsCheckbox :: forall cell. T.ReadWrite cell Boolean => cell -> R.Element
termsCheckbox checked = Left err -> liftEffect
H.div { className: "form-group form-check text-center" } $ here.warn3 "request error" state err
[ F.bindCheckbox { checked, className: "form-check-input" } *> T.write_ (Just err) error
, H.label { className: "form-check-label" }
[ H.text "I hereby accept the " Right session_ -> liftEffect
, H.a { target: "_blank", href: termsUrl } $ Sessions.change (Sessions.Login session_) sessions
[ H.text "terms of use" ] ]] *> T.write_ false visible
where termsUrl = "http://gitlab.iscpif.fr/humanities/tofu/tree/master"
T.write_ false onPending
requestAccessLink :: R.Element
requestAccessLink = -- | Render
H.div { className: "text-center" } -- |
[ H.a { href, target: "_blank" } [ H.text "request access" ] ] pure $
where href = "https://iscpif.fr/apply-for-a-services-account/"
H.div
usernameInput :: forall cell. T.ReadWrite cell String => cell -> R.Element { className: "login-modal-form" }
usernameInput value = [
F.bindInput H.div
{ value { className: "login-modal-form__title" }
, type: "text", className: "form-control" [
, id: "id_username", placeholder: "username" B.iconButton
, name: "username", maxLength: "254" { name: "arrow-left"
, className: "login-modal-form__title__return"
, elevation: Level2
, callback: onReturnClick
}
,
H.span
{ className: "login-modal-form__title__text" }
[
H.text $ "garg://" <> show backend
]
]
,
H.a
{ href: "https://iscpif.fr/apply-for-a-services-account/"
, target: "_blank"
, className: intercalate " "
[ "login-modal-form__request-access"
, "btn btn-primary"
]
} }
[
B.icon
{ name: "hand-o-right" }
,
B.wad_
[ "d-inline-block", "virtual-space", "w-1" ]
,
H.text "Request access"
]
,
H.div
{ className: "login-modal-form__separator" }
[
H.hr
{}
,
H.span
{ className: "login-modal-form__separator__text" }
[ H.text "or" ]
]
,
H.form
{ className: "login-modal-form__form" }
[
-- (?) never used?
-- H.input
-- { type: "hidden"
-- , name: "csrfmiddlewaretoken"
-- , value: csrfMiddlewareToken
-- }
passwordInput :: forall cell. T.ReadWrite cell String => cell -> R.Element -- Username
passwordInput value = H.div
F.bindInput { className: intercalate " "
{ value [ "form-group"
, type: "password", className: "form-control" , (fv.hasError' "username") ?
, name: "password", placeholder: "password" "form-group--error" $
, id: "id_password" mempty
]
}
[
H.div { className: "form-group__label" }
[
H.label {} [ H.text "Username" ]
]
,
H.div { className: "form-group__field" }
[
B.formInput $
{ size: LargeSize
} `Record.merge` bindStateKey "username"
]
]
,
-- Password
H.div
{ className: intercalate " "
[ "form-group"
, (fv.hasError' "password") ?
"form-group--error" $
mempty
]
} }
[
H.div { className: "form-group__label" }
[
H.label {} [ H.text "Password" ]
]
,
H.div { className: "form-group__field" }
[
B.formInput $
{ size: LargeSize
, type: "password"
} `Record.merge` bindStateKey "password"
,
H.a
{ on: { click: onPasswordForgottenClick }
, className: "font-size-95 text-decoration-underline"
}
[ H.text "Password forgotten" ]
]
]
,
-- Error
R2.fromMaybe error' $
loginSubmit :: Boolean -> (ChangeEvent -> Effect Unit) -> R.Element B.div'
loginSubmit isEnabled click = { className: "login-modal-form__error" }
H.button { id ,
, className -- Submit
, disabled: not isEnabled B.button
{ callback: onSubmit
, status: onPending' ? Deferred $ Enabled
, variant: ButtonVariant Primary
, type: "submit" , type: "submit"
, on: { click } } , className: "login-modal-form__log-in"
[ H.text "Login" ] where }
id = "login-button" [
className = "btn btn-primary btn-rounded" B.icon
{ name: "hand-o-right" }
type ForgotPasswordProps = ,
( formType :: T.Box FormType ) B.wad_
[ "d-inline-block", "virtual-space", "w-1" ]
forgotPassword :: R2.Leaf ForgotPasswordProps ,
forgotPassword = R2.leaf forgotPasswordCpt H.text "Log in"
forgotPasswordCpt :: R.Component ForgotPasswordProps ]
forgotPasswordCpt = here.component "forgotPassword" cpt where ]
cpt { formType } _ = do ]
pure $ H.div { className: "" }
[ H.a { className: "", -- R2.row
on: { click } } [ H.text "Forgot password?" ] -- [
-- H.form
-- { className: "col-md-12" }
-- [
-- H.h4
-- { className: "text-center" }
-- [ H.text $ "Login to garg://" <> show backend ]
-- ,
-- H.div
-- { className: "text-center" }
-- [
-- H.a
-- { href: "https://iscpif.fr/apply-for-a-services-account/"
-- , target: "_blank"
-- }
-- [ H.text "request access" ]
-- ]
-- ,
-- H.div
-- { className: "form-group form-check text-center" }
-- [
-- F.bindCheckbox
-- { checked: cursors.agreed
-- , className: "form-check-input"
-- }
-- ,
-- H.label
-- { className: "form-check-label" }
-- [
-- H.text "I hereby accept the "
-- ,
-- H.a
-- { target: "_blank"
-- , href: "http://gitlab.iscpif.fr/humanities/tofu/tree/master"
-- }
-- [ H.text "terms of use" ]
-- ]
-- ]
-- H.div
-- {}
-- [
-- H.a
-- { on: { click: \_ -> T.write_ ForgotPassword formType
-- }
-- }
-- [ H.text "Forgot password?" ]
-- ]
-- ]
-- ]
type FormData =
{ error :: String
, username :: String
, password :: String
, agreed :: Boolean
}
defaultData :: FormData
defaultData =
{ error : ""
, username : ""
, password : ""
, agreed : false
}
formValidation :: FormData -> Effect VForm
formValidation r = foldl append mempty rules
where
rules =
[ FV.nonEmpty "username" r.username
, FV.nonEmpty "password" r.password
, FV.equals "agreed" false r.agreed
] ]
-----------------------------------------------
signin ::
Backend
-> FormData
-> Aff (Either String Session)
signin backend { username, password } = postAuthRequest backend request
where where
click _ = T.write_ ForgotPassword formType -- (?) is `cleanString` necessary?
request
= AuthRequest { username: cleanString username
, password: cleanString password
}
cleanString :: String -> String
cleanString str =
String.replace (String.Pattern " ")
(String.Replacement "") str
...@@ -55,6 +55,6 @@ instance Eq AuthData where ...@@ -55,6 +55,6 @@ instance Eq AuthData where
_AuthData :: Iso' AuthData { token :: Token, tree_id :: TreeId, user_id :: UserId } _AuthData :: Iso' AuthData { token :: Token, tree_id :: TreeId, user_id :: UserId }
_AuthData = iso (\(AuthData v) -> v) AuthData _AuthData = iso (\(AuthData v) -> v) AuthData
data FormType = Login | ForgotPassword data FormType = Login | ForgotPassword | Manager
derive instance Generic FormType _ derive instance Generic FormType _
derive instance Eq FormType derive instance Eq FormType
...@@ -61,6 +61,17 @@ instance maximumString :: Maximum String where ...@@ -61,6 +61,17 @@ instance maximumString :: Maximum String where
| (length input) > max -> pure $ invalid [ field /\ "maximum" ] | (length input) > max -> pure $ invalid [ field /\ "maximum" ]
| otherwise -> pure $ pure unit | otherwise -> pure $ pure unit
-- Regarding Boolean field value
instance equalsBoolean :: Equals Boolean where
equals field box box' = do
input <- T.read box
input' <- T.read box'
case unit of
_
| (not eq input input') -> pure $ invalid [ field /\ "equals" ]
| otherwise -> pure $ pure unit
uppercase :: Field -> T.Box String -> Effect VForm uppercase :: Field -> T.Box String -> Effect VForm
uppercase field = T.read >=> case _ of uppercase field = T.read >=> case _ of
input input
......
...@@ -52,6 +52,13 @@ instance maximumString :: Maximum String where ...@@ -52,6 +52,13 @@ instance maximumString :: Maximum String where
| (length input) > max = pure $ invalid [ field /\ "maximum" ] | (length input) > max = pure $ invalid [ field /\ "maximum" ]
| otherwise = pure $ pure unit | otherwise = pure $ pure unit
-- Regarding Boolean field value
instance equalsBoolean :: Equals Boolean where
equals field input input'
| (not eq input input') = pure $ invalid [ field /\ "equals" ]
| otherwise = pure $ pure unit
uppercase :: Field -> String -> Effect VForm uppercase :: Field -> String -> Effect VForm
uppercase field input uppercase field input
| (toLower input) == input = pure $ invalid [ field /\ "uppercase" ] | (toLower input) == input = pure $ invalid [ field /\ "uppercase" ]
......
...@@ -9,3 +9,71 @@ ...@@ -9,3 +9,71 @@
&__actions &__actions
display: flex display: flex
gap: space-x(3.5) gap: space-x(3.5)
//////////////////////////////////:
.login-modal-form
$cta-width: 200px
$form-width: 520px
&__title
position: relative
background-color: $border-color
padding: $card-spacer-y $card-spacer-x
text-align: center
// (?) dirty negative margins to overlap the ".modal-body" paddings
margin-top: - $modal-inner-padding
margin-left: - $modal-inner-padding
margin-right: - $modal-inner-padding
&__return
@include centered
position: absolute
left: space-x(3.5)
&__text
font-family: $font-family-monospace
font-size: 20px
font-weight: bold
&__separator
padding: $card-spacer-y calc(2* #{ $card-spacer-x })
position: relative
&__text
@include centered
position: absolute
display: inline-block
padding-left: space-x(4)
padding-right: space-x(4)
font-family: $headings-font-family
font-size: 24px
background-color: $modal-content-bg
&__request-access
margin-top: calc(3 * #{ $card-spacer-y})
margin-left: auto
margin-right: auto
margin-bottom: $card-spacer-y
padding: $btn-padding-y-lg $btn-padding-x-lg
width: $cta-width
display: block
&__log-in
padding: $btn-padding-y-lg $btn-padding-x-lg
width: $cta-width
margin-bottom: calc(2 * #{ $card-spacer-y})
margin-left: auto
margin-right: auto
margin-top: $card-spacer-y
display: block
&__form
margin: 0 auto
width: $form-width
&__error
margin-bottom: $form-group-margin-bottom
color: $danger
text-align: center
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment