Commit 65e9cf3e authored by Alexandre Delanoë's avatar Alexandre Delanoë

Merge remote-tracking branch 'origin/600-dev-team-invite-fixes' into dev

parents 16762559 81ad30d2
......@@ -104,7 +104,23 @@ let overrides =
}
let additions =
{ sequences =
{ convertable-options =
{ dependencies = [ "console", "effect", "maybe", "record" ]
, repo = "https://github.com/natefaubion/purescript-convertable-options"
, version = "v1.0.0"
}
, data-default =
{ dependencies =
[ "assert", "lists", "maybe", "record", "effect", "prelude" ]
, repo = "https://github.com/thought2/purescript-data-default"
, version = "350e600a5a022c9599865a2dd14196b442f59bcc"
}
, dom-filereader =
{ dependencies = [ "aff", "arraybuffer-types", "web-file", "web-html" ]
, repo = "https://github.com/nwolverson/purescript-dom-filereader"
, version = "v5.0.0"
}
, sequences =
{ dependencies =
[ "arrays"
, "assert"
......@@ -126,6 +142,28 @@ let additions =
, repo = "https://github.com/garganscript/purescript-sequences.git"
, version = "recursion-fix"
}
, read =
{ dependencies = [ "prelude", "maybe", "strings" ]
, repo = "https://github.com/truqu/purescript-read"
, version = "v1.0.1"
}
, simple-json-generics =
{ dependencies =
[ "assert"
, "control"
, "effect"
, "either"
, "foreign"
, "partial"
, "prelude"
, "simple-json"
, "transformers"
, "typelevel-prelude"
]
, repo =
"https://github.com/garganscript/purescript-simple-json-generics"
, version = "master"
}
, spec-discovery =
{ dependencies = [ "prelude", "effect", "arrays", "spec", "node-fs" ]
, repo = "https://github.com/purescript-spec/purescript-spec-discovery"
......@@ -156,28 +194,6 @@ let additions =
"https://gitlab.iscpif.fr/gargantext/purescript-string-search.git"
, version = "v0.1.6"
}
, dom-filereader =
{ dependencies = [ "aff", "arraybuffer-types", "web-file", "web-html" ]
, repo = "https://github.com/nwolverson/purescript-dom-filereader"
, version = "v5.0.0"
}
, simple-json-generics =
{ dependencies =
[ "assert"
, "control"
, "effect"
, "either"
, "foreign"
, "partial"
, "prelude"
, "simple-json"
, "transformers"
, "typelevel-prelude"
]
, repo =
"https://github.com/garganscript/purescript-simple-json-generics"
, version = "master"
}
, tuples-native =
{ dependencies =
[ "console"
......@@ -191,11 +207,6 @@ let additions =
, repo = "https://github.com/garganscript/purescript-tuples-native"
, version = "v2.3.0"
}
, read =
{ dependencies = [ "prelude", "maybe", "strings" ]
, repo = "https://github.com/truqu/purescript-read"
, version = "v1.0.1"
}
, versions =
{ dependencies = [ "prelude" ]
, repo = "https://github.com/hdgarrood/purescript-versions.git"
......@@ -206,11 +217,6 @@ let additions =
, repo = "https://github.com/mjepronk/purescript-web-url"
, version = "v2.0.0"
}
, convertable-options =
{ dependencies = [ "console", "effect", "maybe", "record" ]
, repo = "https://github.com/natefaubion/purescript-convertable-options"
, version = "v1.0.0"
}
}
in upstream // overrides // additions
......@@ -28,6 +28,7 @@ to generate this file without the comments in this block.
, "convertable-options"
, "css"
, "d3"
, "data-default"
, "datetime"
, "debug"
, "dom-filereader"
......
......@@ -330,7 +330,6 @@ performAction = performAction' where
performAction' (DoSearch task) p = doSearch task p
performAction' (UpdateNode params) p = updateNode params p
performAction' (RenameNode name) p = renameNode name p
performAction' (ShareTeam username) p = shareTeam username p
performAction' (SharePublic { params }) p = sharePublic params p
performAction' (AddContact params) p = addContact params p
performAction' (AddNode name nodeType) p = addNode' name nodeType p
......@@ -385,10 +384,6 @@ performAction = performAction' where
GAT.insert id task tasks
here.log2 "[performAction] UpdateNode task:" task
shareTeam username { boxes: { errors }, nodeId: id, session } = do
eTask <- Share.shareReq session id $ Share.ShareTeamParams { username }
handleRESTError here errors eTask $ \_task -> pure unit
sharePublic params p@{ boxes: { errors }, session } = traverse_ f params where
f (SubTreeOut { in: inId, out }) = do
eTask <- Share.shareReq session inId $ Share.SharePublicParams { node_id: out }
......
......@@ -325,10 +325,6 @@ renameNode name p@{ boxes: { errors }, session, tree: (NTree (LNode {id}) _) } =
handleRESTError here errors eTask $ \_task -> pure unit
refreshTree p
shareTeam username { boxes: { errors }, session, tree: (NTree (LNode {id}) _)} = do
eTask <- Share.shareReq session id $ Share.ShareTeamParams { username }
handleRESTError here errors eTask $ \_task -> pure unit
sharePublic params p@{ boxes: { errors, forestOpen }, session } = traverse_ f params where
f (SubTreeOut { in: inId, out }) = do
eTask <- Share.shareReq session inId $ Share.SharePublicParams { node_id: out }
......@@ -398,7 +394,6 @@ performAction (DeleteNode nt) p = deleteNode' nt p
performAction (DoSearch task) p = doSearch task p
performAction (UpdateNode params) p = updateNode params p
performAction (RenameNode name) p = renameNode name p
performAction (ShareTeam username) p = shareTeam username p
performAction (SharePublic { params }) p = sharePublic params p
performAction (AddContact params) p = addContact params p
performAction (AddNode name nodeType) p = addNode' name nodeType p
......@@ -407,7 +402,6 @@ performAction (UploadFile nodeType fileType fileFormat lang mName contents selec
uploadFile' nodeType fileType fileFormat lang mName contents p selection
performAction (UploadArbitraryFile fileFormat mName blob selection) p =
uploadArbitraryFile' fileFormat mName blob p selection
performAction DownloadNode _ = liftEffect $ here.log "[performAction] DownloadNode"
performAction (MoveNode {params}) p = moveNode params p
performAction (MergeNode {params}) p = mergeNode params p
performAction (LinkNode { nodeType, params }) p = linkNode nodeType params p
......@@ -415,3 +409,5 @@ performAction RefreshTree p = refreshTree p
performAction CloseBox p = closeBox p
performAction (DocumentsFromWriteNodes params) p = documentsFromWriteNodes params p
performAction NoAction _ = liftEffect $ here.log "[performAction] NoAction"
performAction DownloadNode _ = liftEffect $ here.log "[performAction] DownloadNode"
performAction (ShareTeam _) _ = liftEffect $ here.log "[performAction] ShareTeam not used as action, see Node/Action/Share instead"
......@@ -13,7 +13,7 @@ import Effect (Effect)
import Effect.Aff (Aff, launchAff_)
import Gargantext.Components.Forest.Tree.Node.Action.Types (Action(..))
import Gargantext.Components.Forest.Tree.Node.Settings (SettingsBox(..), settingsBox)
import Gargantext.Components.Forest.Tree.Node.Tools (formChoice, panel, submitButton)
import Gargantext.Components.Forest.Tree.Node.Tools as Tools
import Gargantext.Components.InputWithEnter (inputWithEnterWithKey)
import Gargantext.Components.Lang (Lang(..), translate)
import Gargantext.Config.REST (RESTError, AffRESTError)
......@@ -43,9 +43,7 @@ addNodeAsync :: Session
-> AffRESTError GT.AsyncTaskWithType
addNodeAsync session parentId q = do
eTask :: Either RESTError GT.AsyncTask <- post session p q
case eTask of
Left err -> pure $ Left err
Right task -> pure $ Right $ GT.AsyncTaskWithType { task, typ: GT.AddNode }
pure $ (\task -> GT.AsyncTaskWithType { task, typ: GT.AddNode }) <$> eTask
where
p = GR.NodeAPI GT.Node (Just parentId) (GT.asyncTaskTypePath GT.AddNode)
......@@ -91,36 +89,41 @@ addNodeViewCpt = here.component "addNodeView" cpt where
setNodeType' nt = do
T.write_ (GT.prettyNodeType nt) nodeName
T.write_ nt nodeType
(maybeChoose /\ nt') = if length nodeTypes > 1
then ([ formChoice { items: nodeTypes
, default: nodeType'
, callback: setNodeType'
, print: print hasChromeAgent' } [] ] /\ nodeType')
else ([H.div {} [H.text $ "Creating a node of type "
<> show defaultNt
<> " with name:"
]
] /\ defaultNt
)
where
defaultNt = (fromMaybe Error $ head nodeTypes)
maybeEdit = [ if edit
then inputWithEnterWithKey {
onBlur: \val -> T.write_ val nodeName
, onEnter: \_ -> launchAff_ $ dispatch (AddNode nodeName' nt')
, onValueChanged: \val -> T.write_ val nodeName
, autoFocus: true
, className: "form-control"
, defaultValue: nodeName' -- (prettyNodeType nt')
, placeholder: nodeName' -- (prettyNodeType nt')
, type: "text"
, key: show nodeType'
, required: false
}
else H.div {} []
]
pure $ panel (maybeChoose <> maybeEdit) (submitButton (AddNode nodeName' nt') dispatch)
(maybeChoose /\ nt') =
if length nodeTypes > 1 then
[ Tools.formChoice { items: nodeTypes
, default: nodeType'
, callback: setNodeType'
, print: print hasChromeAgent' } []
] /\ nodeType'
else
[ H.div {}
[H.text $ "Creating a node of type "
<> show defaultNt
<> " with name:"
]
] /\ defaultNt
where
defaultNt = fromMaybe Error $ head nodeTypes
maybeEdit =
if edit then
[ inputWithEnterWithKey {
onBlur: \val -> T.write_ val nodeName
, onEnter: \_ -> launchAff_ $ dispatch (AddNode nodeName' nt')
, onValueChanged: \val -> T.write_ val nodeName
, autoFocus: true
, className: "form-control"
, defaultValue: nodeName' -- (prettyNodeType nt')
, placeholder: nodeName' -- (prettyNodeType nt')
, type: "text"
, key: show nodeType'
, required: false
} ]
else []
pure $ Tools.panelWithSubmitButton { action: AddNode nodeName' nt'
, dispatch
, mError: Nothing } (maybeChoose <> maybeEdit)
-- END Create Node
......
......@@ -3,20 +3,21 @@ module Gargantext.Components.Forest.Tree.Node.Action.Contact where
import Prelude
import Data.Maybe (Maybe(..))
import Effect.Aff (Aff, launchAff)
import Effect.Aff (Aff, launchAff_)
import Formula as F
import Reactix as R
import Reactix.DOM.HTML as H
import Toestand as T
import Gargantext.Components.Forest.Tree.Node.Action.Types (Action(..))
import Gargantext.Components.Forest.Tree.Node.Action.Contact.Types (AddContactParams(..))
import Gargantext.Components.Forest.Tree.Node.Action.Types (Action(..))
import Gargantext.Components.Forest.Tree.Node.Tools as Tools
import Gargantext.Components.InputWithEnter (inputWithEnter)
import Gargantext.Config.REST (AffRESTError)
import Gargantext.Routes as GR
import Gargantext.Sessions (Session, post)
import Gargantext.Types (ID)
import Gargantext.Types as GT
import Gargantext.Utils.Reactix as R2
import Reactix as R
import Reactix.DOM.HTML as H
import Toestand as T
here :: R2.Here
here = R2.here "Gargantext.Components.Forest.Tree.Node.Action.Contact"
......@@ -34,21 +35,19 @@ actionAddContact = R.createElement actionAddContactCpt
actionAddContactCpt :: R.Component ActionAddContact
actionAddContactCpt = here.component "actionAddContact" cpt where
cpt { dispatch, id } _ = do
isOpen <- T.useBox true
pure $ textInputBox
{ boxAction: \p -> AddContact p
, boxName:"addContact"
, dispatch
, id
, isOpen
, params: {firstname:"First Name", lastname: "Last Name"} }
pure $
Tools.panelNoFooter { mError: Nothing }
[ textInputBox
{ boxAction: \p -> AddContact p
, dispatch
, id
, params: { firstname: "", lastname: "" } }
]
type TextInputBoxProps =
( boxAction :: AddContactParams -> Action
, boxName :: String
, dispatch :: Action -> Aff Unit
, id :: ID
, isOpen :: T.Box Boolean
, params :: Record AddContactProps )
type AddContactProps = ( firstname :: String, lastname :: String )
......@@ -57,38 +56,50 @@ textInputBox :: R2.Leaf TextInputBoxProps
textInputBox = R2.leaf textInputBoxCpt
textInputBoxCpt :: R.Component TextInputBoxProps
textInputBoxCpt = here.component "textInputBox" cpt where
cpt { boxName, boxAction, dispatch, isOpen
, params: { firstname, lastname } } _ =
content <$> T.useLive T.unequal isOpen
<*> T.useBox firstname <*> T.useBox lastname
cpt { boxAction, dispatch
, params: { firstname, lastname } } _ = do
firstName <- T.useBox firstname
lastName <- T.useBox lastname
let submitF unit = do
f <- T.read firstName
l <- T.read lastName
launchAff_ $
dispatch (boxAction $ AddContactParams { firstname: f, lastname: l })
pure $ H.div { className: "from-group" }
[ textInput "First name" firstName firstname submitF true
, textInput "Last name" lastName lastname submitF false
, R2.row [
submitBtn submitF
]
]
where
content false _ _ = H.div {} []
content true firstName lastName =
H.div { className: "from-group row" }
[ textInput firstName
, textInput lastName
, submitBtn firstName lastName
, cancelBtn
] where
textInput value =
H.div {className: "col-md-8"}
[ F.bindInput
{ value, className: "form-control", type: "text"
, placeholder: (boxName <> " Node") } ]
submitBtn first last =
H.a
{ className: "btn glyphitem fa fa-ok col-md-2 pull-left"
, type: "button", on: { click }, title:"Submit"
} [] where
click _ = do
f <- T.read first
l <- T.read last
T.write_ false isOpen
launchAff $
dispatch (boxAction $ AddContactParams { firstname: f, lastname: l })
cancelBtn =
H.a
{ className: "btn text-danger glyphitem fa fa-remove col-md-2 pull-left"
, on: { click }, title: "Cancel", type: "button"
} [] where
click _ = T.write_ false isOpen
textInput placeholder value defaultValue submitF autoFocus =
R2.row
[ R2.col 8
[ inputWithEnter { onBlur: \s -> T.write_ s value
, onEnter: submitF
, onValueChanged: \s -> T.write_ s value
, autoFocus
, className: "form-control"
, defaultValue
, placeholder
, type: "value"
, required: true }
]
-- [ F.bindInput
-- { value
-- , className: "form-control"
-- , type: "text"
-- , placeholder: boxName <> " Node" }
-- ]
]
submitBtn submitF =
H.a { className: "btn glyphitem fa fa-send col-md-2 pull-left"
, type: "button"
, on: { click: \_ -> submitF unit }
, title:"Submit"
} []
......@@ -6,7 +6,7 @@ import Gargantext.Prelude
import Data.Maybe (Maybe(..))
import Effect.Aff (Aff)
import Gargantext.Components.Forest.Tree.Node.Action.Types (Action(..))
import Gargantext.Components.Forest.Tree.Node.Tools (submitButton, panel)
import Gargantext.Components.Forest.Tree.Node.Tools as Tools
import Gargantext.Config.REST (AffRESTError)
import Gargantext.Routes (SessionRoute(..))
import Gargantext.Sessions (Session, delete, put_)
......@@ -53,21 +53,26 @@ actionDeleteUser = R.createElement actionDeleteUserCpt
actionDeleteUserCpt :: R.Component Delete
actionDeleteUserCpt = here.component "actionDeleteUser" cpt where
cpt _ _ = do
pure $ panel [ H.div { style: {margin: "10px"}}
[ H.text $ "Yes, we are RGPD compliant!"
<> " But you can not delete User Node yet."
<> " We are still on development."
<> " Thanks for your comprehensin."
]
] (H.div {} [])
pure $
Tools.panelNoFooter { mError: Nothing }
[ H.div { style: {margin: "10px"}}
[ H.text $ "Yes, we are RGPD compliant!"
<> " But you can not delete User Node yet."
<> " We are still on development."
<> " Thanks for your comprehensin."
]
]
actionDeleteOther :: R2.Component Delete
actionDeleteOther = R.createElement actionDeleteOtherCpt
actionDeleteOtherCpt :: R.Component Delete
actionDeleteOtherCpt = here.component "actionDeleteOther" cpt where
cpt { dispatch, nodeType } _ = do
pure $ panel (map (\t -> H.p {} [H.text t])
[ "Are your sure you want to delete it ?"
, "If yes, click again below."
]
) (submitButton (DeleteNode nodeType) dispatch)
pure $
Tools.panelWithSubmitButton { action: DeleteNode nodeType
, dispatch
, mError: Nothing }
(map (\t -> H.p {} [H.text t])
[ "Are your sure you want to delete it ?"
, "If yes, click again below."
])
module Gargantext.Components.Forest.Tree.Node.Action.Documentation where
import Reactix as R
import Reactix.DOM.HTML as H
import Data.Maybe (Maybe(..))
import Gargantext.Components.Forest.Tree.Node.Tools as Tools
import Gargantext.Prelude (map, pure, show, ($), (<>))
import Gargantext.Components.Forest.Tree.Node.Tools (panel)
import Gargantext.Types (NodeType)
import Gargantext.Types as GT
import Gargantext.Utils.Reactix as R2
import Reactix as R
import Reactix.DOM.HTML as H
here :: R2.Here
here = R2.here "Gargantext.Components.Forest.Tree.Node.Action.Documentation"
......@@ -22,10 +21,10 @@ actionDoc = R.createElement actionDocCpt
actionDocCpt :: R.Component ActionDoc
actionDocCpt = here.component "actionDoc" cpt where
cpt { nodeType } _ = do
pure $ panel ([ infoTitle nodeType ]
<> (map (\info -> H.p {} [H.text info]) $ docOf nodeType)
)
(H.div {} [])
pure $
Tools.panelNoFooter { mError: Nothing }
([ infoTitle nodeType ]
<> (map (\info -> H.p {} [ H.text info ]) $ docOf nodeType))
where
infoTitle :: NodeType -> R.Element
infoTitle nt = H.div { style: {margin: "10px"}}
......
......@@ -5,7 +5,7 @@ import Data.Maybe (Maybe(..))
import Data.Show.Generic (genericShow)
import Data.String.Common (toLower)
import Gargantext.Components.Forest.Tree.Node.Action.Types (Action(DownloadNode))
import Gargantext.Components.Forest.Tree.Node.Tools (fragmentPT, panel, submitButtonHref)
import Gargantext.Components.Forest.Tree.Node.Tools as Tools
import Gargantext.Ends (url)
import Gargantext.Prelude
import Gargantext.Routes as Routes
......@@ -41,8 +41,11 @@ actionDownloadCorpus = R.createElement actionDownloadCorpusCpt
actionDownloadCorpusCpt :: R.Component ActionDownload
actionDownloadCorpusCpt = here.component "actionDownloadCorpus" cpt where
cpt { id, session } _ = do
pure $ panel [H.div {} [H.text info]]
(submitButtonHref DownloadNode href)
pure $
Tools.panelWithSubmitButtonHref { action: DownloadNode
, href
, mError: Nothing }
[ H.div {} [H.text info] ]
where
href = url session $ Routes.NodeAPI GT.Corpus (Just id) "export"
info = "Download as JSON"
......@@ -52,8 +55,11 @@ actionDownloadGraph = R.createElement actionDownloadGraphCpt
actionDownloadGraphCpt :: R.Component ActionDownload
actionDownloadGraphCpt = here.component "actionDownloadGraph" cpt where
cpt { id, session } _ = do
pure $ panel [H.div {} [H.text info]]
(submitButtonHref DownloadNode href)
pure $
Tools.panelWithSubmitButtonHref { action: DownloadNode
, href
, mError: Nothing }
[ H.div {} [H.text info] ]
where
href = url session $ Routes.NodeAPI GT.Graph (Just id) "gexf"
info = "Info about the Graph as GEXF format"
......@@ -79,15 +85,17 @@ actionDownloadNodeListCpt = here.component "actionDownloadNodeList" cpt where
downloadFormat <- T.useBox NL_JSON
downloadFormat' <- T.useLive T.unequal downloadFormat
pure $ panel
[ R2.select { className: "form-control"
, defaultValue: show downloadFormat'
, on: { change: onChange downloadFormat } }
[ opt NL_CSV downloadFormat
, opt NL_JSON downloadFormat ]
, H.div {} [ H.text $ info downloadFormat' ]
]
(submitButtonHref DownloadNode $ href downloadFormat')
pure $
Tools.panelWithSubmitButtonHref { action: DownloadNode
, href: href downloadFormat'
, mError: Nothing }
[ R2.select { className: "form-control"
, defaultValue: show downloadFormat'
, on: { change: onChange downloadFormat } }
[ opt NL_CSV downloadFormat
, opt NL_JSON downloadFormat ]
, H.div {} [ H.text $ info downloadFormat' ]
]
where
opt t downloadFormat = H.option { value: show t } [ H.text $ show t ]
where
......@@ -122,7 +130,9 @@ actionDownloadNodeTextsCpt = here.component "actionDownloadNodeTexts" cpt where
downloadFormat <- T.useBox NT_JSON
downloadFormat' <- T.useLive T.unequal downloadFormat
pure $ panel
pure $ Tools.panelWithSubmitButtonHref { action: DownloadNode
, href: href downloadFormat'
, mError: Nothing }
[ R2.select { className: "form-control"
, defaultValue: show downloadFormat'
, on: { change: onChange downloadFormat } }
......@@ -130,7 +140,6 @@ actionDownloadNodeTextsCpt = here.component "actionDownloadNodeTexts" cpt where
, opt NT_JSON downloadFormat ]
, H.div {} [ H.text $ info downloadFormat' ]
]
(submitButtonHref DownloadNode $ href downloadFormat')
where
opt t downloadFormat = H.option { value: show t } [ H.text $ show t ]
where
......@@ -154,4 +163,4 @@ actionDownloadOther = R.createElement actionDownloadOtherCpt
actionDownloadOtherCpt :: R.Component ActionDownload
actionDownloadOtherCpt = here.component "actionDownloadOther" cpt where
cpt _ _ = do
pure $ fragmentPT $ "Soon, you will be able to download your file here "
pure $ Tools.fragmentPT $ "Soon, you will be able to download your file here "
......@@ -6,7 +6,7 @@ import Data.Either (Either)
import Data.Maybe (Maybe(..))
import Gargantext.Components.Forest.Tree.Node.Action.Types (Action(..))
import Gargantext.Components.Forest.Tree.Node.Action.Update.Types (LinkNodeReq(..), UpdateNodeParams(..))
import Gargantext.Components.Forest.Tree.Node.Tools (submitButton, panel)
import Gargantext.Components.Forest.Tree.Node.Tools as Tools
import Gargantext.Components.Forest.Tree.Node.Tools.SubTree (subTreeView, SubTreeParamsIn)
import Gargantext.Components.Forest.Tree.Node.Tools.SubTree.Types (SubTreeOut(..))
import Gargantext.Config.REST (AffRESTError, RESTError)
......@@ -69,23 +69,26 @@ linkNodeCpt' = here.component "__clone__" cpt
let
button = case action' of
LinkNode { params } -> case params of
Just (SubTreeOut { in: inId }) -> submitButton
(toParams nodeType inId)
dispatch
Nothing -> mempty
LinkNode { params } ->
R2.fromMaybe params $
\(SubTreeOut { in: inId }) ->
Tools.submitButton { action: toParams nodeType inId
, dispatch }
_ -> mempty
pure $ panel [
subTreeView { action
, boxes
, dispatch
, id
, nodeType
, session
, subTreeParams
} []
] button
pure $
Tools.panel { mError: Nothing }
[ subTreeView { action
, boxes
, dispatch
, id
, nodeType
, session
, subTreeParams
} []
-- footer
, button ]
toParams :: GT.NodeType -> GT.ID -> Action
toParams nodeType id
......
......@@ -4,6 +4,7 @@ import Gargantext.Prelude
import Data.Array (filter, null, (:))
import Data.Either (Either(..))
import Data.Maybe (Maybe(..))
import Effect.Aff (runAff_)
import Effect.Class (liftEffect)
import Gargantext.Components.Forest.Tree.Node.Tools as Tools
......@@ -52,52 +53,53 @@ type TeamProps =
teamLayoutWrapper :: R2.Component TeamProps
teamLayoutWrapper = R.createElement teamLayoutWrapperCpt
teamLayoutWrapperCpt :: R.Component TeamProps
teamLayoutWrapperCpt = here.component "teamLayoutWrapper" cpt where
cpt {nodeId, session, team: {team_owner_username, team_members}} _ = do
teamS <- T.useBox team_members
team' <- T.useLive T.unequal teamS
error <- T.useBox ""
error' <- T.useLive T.unequal error
team_members <- T.useBox team_members
error <- T.useBox Nothing
pure $ teamLayoutRows {nodeId, session, team: teamS, team', error, error', team_owner_username}
pure $ teamLayoutRows {nodeId, session, team_members, error, team_owner_username}
type TeamRowProps =
( nodeId :: ID
, session :: Session
, team :: T.Box (Array TeamMember)
, error :: T.Box String
, team' :: Array TeamMember
, error' :: String
, team_members :: T.Box (Array TeamMember)
, error :: T.Box (Maybe String)
, team_owner_username :: String
)
teamLayoutRows :: R2.Leaf TeamRowProps
teamLayoutRows = R2.leaf teamLayoutRowsCpt
teamLayoutRowsCpt :: R.Component TeamRowProps
teamLayoutRowsCpt = here.component "teamLayoutRows" cpt where
cpt { team, nodeId, session, error, team', error', team_owner_username} _ = do
case null team' of
true -> pure $ H.div { style: {margin: "10px"}}
[ H.h4 {} [H.text "Your team is empty, you can send some invitations."]]
false -> pure $ Tools.panel (makeLeader team_owner_username : (map makeTeam team')) (H.div {} [H.text error'])
cpt { team_members, nodeId, session, error, team_owner_username } _ = do
team_members' <- T.useLive T.unequal team_members
error' <- T.useLive T.unequal error
pure $
if null team_members' then
H.div { style: {margin: "10px"}}
[ H.h4 {} [H.text "Your team is empty, you can send some invitations."]]
else
Tools.panelNoFooter { mError: error' }
(makeLeader team_owner_username : (map makeTeam team_members'))
where
makeTeam :: TeamMember -> R.Element
makeTeam { username, shared_folder_id } = H.div {className: "from-group row"} [ H.div { className: "col-8" } [ H.text username ]
, H.a { className: "text-danger col-2 fa fa-times"
, title: "Remove user from team"
, type: "button"
, on: {click: submit shared_folder_id }
} []
]
makeLeader username = H.div {className: "from-group row"} [ H.div { className: "col-8"} [ H.text username ]
, H.p { className: "col-2"} [ H.text "owner"]
]
makeTeam { username, shared_folder_id } =
H.div {className: "from-group row"} [ H.div { className: "col-8" } [ H.text username ]
, H.a { className: "text-danger col-2 fa fa-times"
, title: "Remove user from team"
, type: "button"
, on: {click: submit shared_folder_id }
} []
]
makeLeader username =
H.div {className: "from-group row"} [ H.div { className: "col-8"} [ H.text username ]
, H.p { className: "col-2"} [ H.text "owner"]
]
submit sharedFolderId _ = do
runAff_ callback $ saveDeleteTeam { session, nodeId, sharedFolderId }
......@@ -105,14 +107,15 @@ teamLayoutRowsCpt = here.component "teamLayoutRows" cpt where
callback res =
case res of
Left _ -> do
_ <- liftEffect $ T.write "Only the Team owner can remove users" error
_ <- liftEffect $ T.write (Just "Only the Team owner can remove users") error
pure unit
Right val ->
case val of
Left _ -> do
pure unit
Right r -> do
T.write_ (filter (\tm -> tm.shared_folder_id /= r) team') team
T.modify_ (filter (\tm -> tm.shared_folder_id /= r)) team_members
T.write_ Nothing error
-------------------------------------------------------------
......
......@@ -5,7 +5,7 @@ import Gargantext.Prelude
import Data.Maybe (Maybe(..))
import Data.Set as Set
import Gargantext.Components.Forest.Tree.Node.Action.Types (Action(..))
import Gargantext.Components.Forest.Tree.Node.Tools (submitButton, panel, checkbox, checkboxesListGroup)
import Gargantext.Components.Forest.Tree.Node.Tools as Tools
import Gargantext.Components.Forest.Tree.Node.Tools.SubTree (subTreeView, SubTreeParamsIn)
import Gargantext.Config.REST (AffRESTError)
import Gargantext.Routes (SessionRoute(..))
......@@ -36,12 +36,13 @@ mergeNodeCpt = here.component "mergeNode" cpt
options <- T.useBox (Set.singleton GT.MapTerm)
let button = case action' of
MergeNode {params} -> case params of
Just val -> submitButton (MergeNode {params: Just val}) dispatch
Nothing -> H.div {} []
MergeNode { params } ->
R2.fromMaybe params $
\val -> Tools.submitButton { action: MergeNode {params: Just val }
, dispatch }
_ -> H.div {} []
pure $ panel
pure $ Tools.panel { mError: Nothing }
[ subTreeView { action
, boxes
, dispatch
......@@ -50,23 +51,24 @@ mergeNodeCpt = here.component "mergeNode" cpt
, session
, subTreeParams
} []
, H.ul { className:"merge mx-auto list-group"}
([ H.li { className: "list-group-item" }
[ H.h5 { className: "mb-1" } [ H.text "Merge which list?" ]
, checkboxesListGroup { groups: [ GT.MapTerm, GT.CandidateTerm, GT.StopTerm ]
, options } []
]
])
, H.ul { className:"merge mx-auto list-group"}
[ H.li { className: "list-group-item" }
[ H.h5 { className: "mb-1" } [ H.text "Title" ]
]
, H.li { className: "list-group-item" }
[ H.div { className: " form-check" }
[ checkbox { value: merge }
, H.label { className: "form-check-label" } [ H.text "Merge data?" ]
]
]
]
]
button
, H.ul { className:"merge mx-auto list-group"}
[ H.li { className: "list-group-item" }
[ H.h5 { className: "mb-1" } [ H.text "Merge which list?" ]
, Tools.checkboxesListGroup { groups: [ GT.MapTerm, GT.CandidateTerm, GT.StopTerm ]
, options } []
]
]
, H.ul { className:"merge mx-auto list-group"}
[ H.li { className: "list-group-item" }
[ H.h5 { className: "mb-1" } [ H.text "Title" ]
]
, H.li { className: "list-group-item" }
[ H.div { className: " form-check" }
[ Tools.checkbox { value: merge }
, H.label { className: "form-check-label" } [ H.text "Merge data?" ]
]
]
]
-- footer
, button ]
......@@ -7,7 +7,7 @@ import Gargantext.Prelude
import Data.Maybe (Maybe(..))
import Gargantext.Components.Forest.Tree.Node.Action.Types (Action(..))
import Gargantext.Components.Forest.Tree.Node.Tools (submitButton, panel)
import Gargantext.Components.Forest.Tree.Node.Tools as Tools
import Gargantext.Components.Forest.Tree.Node.Tools.SubTree (SubTreeParamsIn, subTreeView)
import Gargantext.Config.REST (AffRESTError)
import Gargantext.Routes (SessionRoute(..))
......@@ -59,15 +59,16 @@ moveNodeCpt' = here.component "__clone__" cpt where
action' <- T.useLive T.unequal action
let button = case action' of
MoveNode { params } -> case params of
Just val -> submitButton (MoveNode {params: Just val}) dispatch
Nothing -> H.div {} []
_ -> H.div {} []
MoveNode { params } ->
R2.fromMaybe params $
\val -> Tools.submitButton { action: MoveNode {params: Just val}
, dispatch }
_ -> H.div {} []
pure $
panel
[ subTreeView { action
Tools.panel { mError: Nothing }
[ subTreeView { action
, boxes
, dispatch
, id
......@@ -75,4 +76,6 @@ moveNodeCpt' = here.component "__clone__" cpt where
, session
, subTreeParams
} []
] button
-- footer
, button ]
......@@ -4,16 +4,20 @@ import Gargantext.Prelude
import Data.Array (filter, nub)
import Data.Generic.Rep (class Generic)
import Data.Either (Either(..))
import Data.Maybe (Maybe(..))
import Data.Show.Generic (genericShow)
import Data.String (Pattern(..), contains, trim)
import Data.Tuple.Nested ((/\))
import Effect.Aff (Aff)
import Effect.Aff (Aff, launchAff_)
import Effect.Class (liftEffect)
import Gargantext.Components.Bootstrap as B
import Gargantext.Components.Bootstrap.Types (Elevation(Level1))
import Gargantext.Components.Forest.Tree.Node.Action.Types (Action)
import Gargantext.Components.Forest.Tree.Node.Action.Types as Action
import Gargantext.Components.Forest.Tree.Node.Tools as Tools
import Gargantext.Components.Forest.Tree.Node.Tools.SubTree (subTreeView, SubTreeParamsIn)
import Gargantext.Components.InputWithAutocomplete (inputWithAutocomplete')
import Gargantext.Components.InputWithAutocomplete (inputWithAutocomplete)
import Gargantext.Config.REST (AffRESTError, logRESTError)
import Gargantext.Hooks.Loader (useLoader)
import Gargantext.Routes as GR
......@@ -39,9 +43,6 @@ getCompletionsReq :: { session :: Session } -> AffRESTError (Array String)
getCompletionsReq { session } =
get session GR.Members
shareAction :: String -> Action
shareAction username = Action.ShareTeam (trim username)
------------------------------------------------------------------------
data ShareNodeParams = ShareTeamParams { username :: String }
......@@ -59,27 +60,26 @@ instance Show ShareNodeParams where show = genericShow
------------------------------------------------------------------------
type ShareNode =
( id :: ID
, dispatch :: Action -> Aff Unit
, session :: Session)
, session :: Session )
shareNode :: R2.Component ShareNode
shareNode = R.createElement shareNodeCpt
shareNodeCpt :: R.Component ShareNode
shareNodeCpt = here.component "shareNode" cpt
where
cpt {session, dispatch} _ = do
cpt { id, session } _ = do
useLoader {
loader: getCompletionsReq
, path: { session }
, render: \completions -> shareNodeInner {completions, dispatch} []
, render: \completions -> shareNodeInner { completions, id, session } []
, errorHandler
}
where
errorHandler = logRESTError here "[shareNode]"
type ShareNodeInner =
( dispatch :: Action -> Aff Unit
, completions :: Array String
( completions :: Array String
| ShareNode
)
shareNodeInner :: R2.Component ShareNodeInner
......@@ -87,25 +87,42 @@ shareNodeInner = R.createElement shareNodeInnerCpt
shareNodeInnerCpt :: R.Component ShareNodeInner
shareNodeInnerCpt = here.component "shareNodeInner" cpt
where
cpt { dispatch, completions } _ = do
state <- T.useBox ""
cpt { completions, id, session } _ = do
state' /\ state <- R2.useBox' ""
text' /\ text <- R2.useBox' ""
mError' /\ mError <- R2.useBox' Nothing
pure $ Tools.panel { mError: mError' }
[ inputWithAutocomplete { autoFocus: true
, autocompleteSearch
, classes: "share-users-completions d-flex align-items-center"
, onAutocompleteClick
, onEnterPress: onEnterPress text mError
, pattern: "^\\S+$" -- pattern doesn't allow space characters
, placeholder: "username or email"
, state
, title }
[ B.iconButton { callback: \_ -> onEnterPress text mError state'
, elevation: Level1
, name: "send"
, title: "Submit" } ]
pure $ Tools.panel
[ inputWithAutocomplete' { boxAction: shareAction
, dispatch
, state
, classes: "share-users-completions d-flex align-items-center"
, autocompleteSearch
, onAutocompleteClick
, text
, pattern: "^\\S+$" -- pattern doesn't allow space characters
, title: "Enter a username or an email address (space characters are not allowed)"
, placeholder: "username or email"}
] (H.div {} [H.text text'])
-- footer
, H.div {} [ H.text text' ] ]
where
autocompleteSearch input = pure $ nub $ filter (contains (Pattern input)) completions
onAutocompleteClick _ = pure unit
onEnterPress text mError val = do
T.write_ Nothing mError
launchAff_ do
eRes <- shareReq session id $ ShareTeamParams { username: val }
liftEffect $ case eRes of
Left err -> do
T.write_ (Just $ show err) mError
Right _ -> do
T.write_ ("Invited " <> val <> " to the team") text
T.write_ Nothing mError
title = "Enter a username or an email address (space characters are not allowed)"
------------------------------------------------------------------------
publishNode :: R2.Component SubTreeParamsIn
publishNode = R.createElement publishNodeCpt
......@@ -117,12 +134,13 @@ publishNodeCpt = here.component "publishNode" cpt
action' <- T.useLive T.unequal action
let button = case action' of
Action.SharePublic { params } -> case params of
Just val -> Tools.submitButton (Action.SharePublic {params: Just val}) dispatch
Nothing -> H.div {} []
Action.SharePublic { params } ->
R2.fromMaybe params $
\val -> Tools.submitButton { action: Action.SharePublic {params: Just val}
, dispatch }
_ -> H.div {} []
pure $ Tools.panel
pure $ Tools.panel { mError: Nothing }
[ subTreeView { action
, boxes
, dispatch
......@@ -131,4 +149,6 @@ publishNodeCpt = here.component "publishNode" cpt
, session
, subTreeParams
} []
] button
-- footer
, button ]
......@@ -11,7 +11,7 @@ import Effect (Effect)
import Effect.Aff (Aff, launchAff_)
import Gargantext.Components.Bootstrap.Types (ComponentStatus(..))
import Gargantext.Components.Forest.Tree.Node.Action.Types (Action(..))
import Gargantext.Components.Forest.Tree.Node.Tools (formChoiceSafe, submitButton, panel)
import Gargantext.Components.Forest.Tree.Node.Tools as Tools
import Gargantext.Components.PhyloExplorer.API as Phylo
import Gargantext.Components.PhyloExplorer.Config.ConfigForm as PhyloForm
import Gargantext.Components.PhyloExplorer.ConfigFormParser as PhyloHook
......@@ -62,13 +62,16 @@ updateDashboardCpt = here.component "updateDashboard" cpt where
methodBoard <- T.useBox All
methodBoard' <- T.useLive T.unequal methodBoard
pure $ panel [ -- H.text "Update with"
formChoiceSafe { items: [All, Sources, Authors, Institutes, Ngrams]
, default: methodBoard'
, callback: \val -> T.write_ val methodBoard
, print: show } []
]
(submitButton (UpdateNode $ UpdateNodeParamsBoard { methodBoard: methodBoard' }) dispatch)
pure $
Tools.panelWithSubmitButton { action: UpdateNode $ UpdateNodeParamsBoard { methodBoard: methodBoard' }
, dispatch
, mError: Nothing }
[ -- H.text "Update with"
Tools.formChoiceSafe { items: [All, Sources, Authors, Institutes, Ngrams]
, default: methodBoard'
, callback: \val -> T.write_ val methodBoard
, print: show } []
]
updateGraph :: R2.Component UpdateProps
updateGraph = R.createElement updateGraphCpt
......@@ -97,12 +100,24 @@ updateGraphCpt = here.component "updateGraph" cpt where
callback :: Action -> Aff Unit
callback = dispatch >=> \_ -> dispatch CloseBox
pure $ panel [ H.text "Show subjects with Order1 or concepts with Order2 ?"
, formChoiceSafe { items: [Order1, Order2_A, Order2_B]
, default: methodGraphMetric'
, callback: \val -> T.write_ val methodGraphMetric
, print: show } []
let action = UpdateNode $ UpdateNodeParamsGraph { methodGraphMetric: methodGraphMetric'
, methodGraphClustering: methodGraphClustering'
, methodGraphBridgeness: methodGraphBridgeness'
, methodGraphEdgesStrength : methodGraphEdgesStrength'
, methodGraphNodeType1 : methodGraphNodeType1'
, methodGraphNodeType2 : methodGraphNodeType2'
}
pure $
Tools.panelWithSubmitButton { action
, dispatch: callback
, mError: Nothing }
[ H.text "Show subjects with Order1 or concepts with Order2 ?"
, Tools.formChoiceSafe { items: [Order1, Order2_A, Order2_B]
, default: methodGraphMetric'
, callback: \val -> T.write_ val methodGraphMetric
, print: show } []
]
{-
......@@ -138,16 +153,6 @@ updateGraphCpt = here.component "updateGraph" cpt where
, callback: \val -> T.write_ val methodGraphClustering
, print: show } []
-}
]
(submitButton (UpdateNode $ UpdateNodeParamsGraph { methodGraphMetric: methodGraphMetric'
, methodGraphClustering: methodGraphClustering'
, methodGraphBridgeness: methodGraphBridgeness'
, methodGraphEdgesStrength : methodGraphEdgesStrength'
, methodGraphNodeType1 : methodGraphNodeType1'
, methodGraphNodeType2 : methodGraphNodeType2'
}
) callback
)
......@@ -214,13 +219,16 @@ updateNodeListCpt = here.component "updateNodeList" cpt where
methodList <- T.useBox Basic
methodList' <- T.useLive T.unequal methodList
pure $ panel [ -- H.text "Update with"
formChoiceSafe { items: [Basic, Advanced, WithModel]
, default: methodList'
, callback: \val -> T.write_ val methodList
, print: show } []
]
(submitButton (UpdateNode $ UpdateNodeParamsList { methodList: methodList' }) dispatch)
pure $
Tools.panelWithSubmitButton { action: UpdateNode $ UpdateNodeParamsList { methodList: methodList' }
, dispatch
, mError: Nothing }
[ -- H.text "Update with"
Tools.formChoiceSafe { items: [Basic, Advanced, WithModel]
, default: methodList'
, callback: \val -> T.write_ val methodList
, print: show } []
]
updateTexts :: R2.Component UpdateProps
updateTexts = R.createElement updateTextsCpt
......@@ -230,14 +238,16 @@ updateTextsCpt = here.component "updateTexts" cpt where
-- methodTexts <- T.useBox NewNgrams
-- methodTexts' <- T.useLive T.unequal methodTexts
pure $ panel [ -- H.text "Update with"
-- formChoiceSafe { items: [NewNgrams, NewTexts, Both]
-- , default: methodTexts'
-- , callback: \val -> T.write_ val methodTexts
-- , print: show } []
]
-- (submitButton (UpdateNode $ UpdateNodeParamsTexts { methodTexts: methodTexts' }) dispatch)
(submitButton (UpdateNode $ UpdateNodeParamsTexts { methodTexts: Both }) dispatch)
pure $
Tools.panelWithSubmitButton { action: UpdateNode $ UpdateNodeParamsTexts { methodTexts: Both }
, dispatch
, mError: Nothing }
[] -- H.text "Update with"
-- formChoiceSafe { items: [NewNgrams, NewTexts, Both]
-- , default: methodTexts'
-- , callback: \val -> T.write_ val methodTexts
-- , print: show } []
-- (submitButton (UpdateNode $ UpdateNodeParamsTexts { methodTexts: methodTexts' }) dispatch)
updateOther :: R2.Component UpdateProps
......
......@@ -23,7 +23,7 @@ import Gargantext.Components.Forest.Tree.Node.Action (Props)
import Gargantext.Components.Forest.Tree.Node.Action.Types (Action(..))
import Gargantext.Components.Forest.Tree.Node.Action.Upload.Types (FileFormat(..), FileType(..), UploadFileBlob(..), readUFBAsBase64, readUFBAsText)
import Gargantext.Components.Forest.Tree.Node.Action.Utils (loadLanguages)
import Gargantext.Components.Forest.Tree.Node.Tools (fragmentPT, formChoiceSafe, panel)
import Gargantext.Components.Forest.Tree.Node.Tools as Tools
import Gargantext.Components.Lang (Lang(..), langReader)
import Gargantext.Components.ListSelection as ListSelection
import Gargantext.Components.ListSelection.Types (Selection(..))
......@@ -83,7 +83,7 @@ actionUploadOther = R.createElement actionUploadOtherCpt
actionUploadOtherCpt :: R.Component ActionUpload
actionUploadOtherCpt = here.component "actionUploadOther" cpt where
cpt _ _ = do
pure $ fragmentPT $ "Soon, upload for this NodeType."
pure $ Tools.fragmentPT $ "Soon, upload for this NodeType."
-- file upload types
......@@ -153,30 +153,30 @@ uploadFileViewWithLangsCpt = here.component "uploadFileViewWithLangs" cpt
]
, R2.row
[ H.div {className:"col-6 flex-space-around"}
[ formChoiceSafe { items: [ CSV
, CSV_HAL
, Istex
, WOS
, JSON
-- , Iramuteq
]
[ Tools.formChoiceSafe { items: [ CSV
, CSV_HAL
, Istex
, WOS
, JSON
-- , Iramuteq
]
, default: CSV
, callback: setFileType'
, print: show } []
, formChoiceSafe { items: [ Plain
, ZIP ]
, default: Plain
, callback: setFileFormat'
, print: show } []
, Tools.formChoiceSafe { items: [ Plain
, ZIP ]
, default: Plain
, callback: setFileFormat'
, print: show } []
]
]
, R2.row
[ H.div {className:"col-6 flex-space-around"}
[ formChoiceSafe { items: langs <> [No_extraction]
, default: EN
, callback: setLang'
, print: show
} []
[ Tools.formChoiceSafe { items: langs <> [No_extraction]
, default: EN
, callback: setLang'
, print: show
} []
]
]
, R2.row
......@@ -194,7 +194,7 @@ uploadFileViewWithLangsCpt = here.component "uploadFileViewWithLangs" cpt
, selection
} []
]
pure $ panel bodies footer
pure $ Tools.panel { mError: Nothing } (bodies <> [ footer ])
onChangeContents :: forall e. T.Box (Maybe UploadFile) -> E.SyntheticEvent_ e -> Effect Unit
......@@ -318,7 +318,7 @@ uploadListViewCpt = here.component "uploadListView" cpt where
-- Render
pure $
panel
Tools.panel { mError: Nothing }
-- Body
[
-- Upload
......@@ -423,9 +423,9 @@ uploadListViewCpt = here.component "uploadListView" cpt where
} []
]
]
]
-- Footer
(
,
H.div
{}
[
......@@ -439,7 +439,7 @@ uploadListViewCpt = here.component "uploadListView" cpt where
, nodeType: GT.Annuaire
} []
]
)
]
-- START File Type View
type FileTypeProps =
......@@ -663,12 +663,12 @@ uploadTermListViewCpt = here.component "uploadTermListView" cpt
}
]
pure $ panel
pure $ Tools.panel { mError: Nothing }
[ H.form {}
[ R2.row [ R2.col 12 [ input ] ]
, R2.row [ R2.col 12 [ uploadTypeHtml ] ]
]
] footer
, footer ]
onChangeContents :: forall e. T.Box (Maybe UploadFile)
-> E.SyntheticEvent_ e
......@@ -815,7 +815,7 @@ uploadFrameCalcViewWithLangsCpt = here.component "uploadFrameCalcViewWithLangs"
[ H.text "Upload!" ]
]
pure $ panel bodies footer
pure $ Tools.panel { mError: Nothing } (bodies <> [ footer ])
where
onClick lang' selection' _ = do
......
......@@ -11,7 +11,7 @@ import Gargantext.Components.App.Store (Boxes)
import Gargantext.Components.Bootstrap as B
import Gargantext.Components.Forest.Tree.Node.Action.Types (Action(..))
import Gargantext.Components.Forest.Tree.Node.Action.Utils (loadLanguages)
import Gargantext.Components.Forest.Tree.Node.Tools (panel, submitButton)
import Gargantext.Components.Forest.Tree.Node.Tools as Tools
import Gargantext.Components.Lang (Lang(..))
import Gargantext.Components.ListSelection as ListSelection
import Gargantext.Components.ListSelection.Types as ListSelection
......@@ -71,80 +71,85 @@ actionWriteNodesDocumentsWithLangsCpt = here.component "actionWriteNodesDocument
paragraphs' /\ paragraphBox
<- R2.useBox' "7"
let bodies = [
H.div
{ className: "col-12 flex-space-around" }
[ H.h4 {}
[ H.text "Will traverse all Write Nodes and insert them as documents into current corpus." ]
]
,
-- lang
H.div
{ className: "form-group" }
[
H.div
{ className: "form-group__label" }
pure $
Tools.panelWithSubmitButton { action: DocumentsFromWriteNodes { id
, lang: lang'
, selection: selection'
, paragraphs: paragraphs' }
, dispatch
, mError: Nothing }
[
B.label_ $
"File lang"
H.div
{ className: "col-12 flex-space-around" }
[ H.h4 {}
[ H.text "Will traverse all Write Nodes and insert them as documents into current corpus." ]
]
,
-- lang
H.div
{ className: "form-group" }
[
H.div
{ className: "form-group__label" }
[
B.label_ $
"File lang"
]
,
H.div
{ className: "form-group__field" }
[
B.formSelect'
{ callback: flip T.write_ langBox
, value: lang'
, list: langs <> [ No_extraction ]
}
[]
]
]
,
-- paragraph
H.div
{ className: "form-group "}
[
H.div
{ className: "form-group__label" }
[
B.label_ $
"Paragraph size (sentences)"
]
,
H.div
{ className: "form-group__field" }
[
B.formInput
{ callback: flip T.write_ paragraphBox
, value: paragraphs'
}
]
]
,
--selection
H.div
{ className: "form-group" }
[
H.div
{ className: "form-group__label" }
[
B.label_ $
"List selection"
]
,
H.div
{ className: "form-group__field" }
[
ListSelection.selection
{ selection: selectionBox
, session
} []
]
]
]
,
H.div
{ className: "form-group__field" }
[
B.formSelect'
{ callback: flip T.write_ langBox
, value: lang'
, list: langs <> [ No_extraction ]
}
[]
]
]
,
-- paragraph
H.div
{ className: "form-group "}
[
H.div
{ className: "form-group__label" }
[
B.label_ $
"Paragraph size (sentences)"
]
,
H.div
{ className: "form-group__field" }
[
B.formInput
{ callback: flip T.write_ paragraphBox
, value: paragraphs'
}
]
]
,
--selection
H.div
{ className: "form-group" }
[
H.div
{ className: "form-group__label" }
[
B.label_ $
"List selection"
]
,
H.div
{ className: "form-group__field" }
[
ListSelection.selection
{ selection: selectionBox
, session
} []
]
]
]
pure $ panel bodies (submitButton (DocumentsFromWriteNodes { id, lang: lang', selection: selection', paragraphs: paragraphs' }) dispatch)
type Params =
......
......@@ -332,31 +332,39 @@ panelAction = R2.leaf panelActionCpt
panelActionCpt :: R.Component PanelActionProps
panelActionCpt = here.component "panelAction" cpt
where
cpt { action: Documentation nodeType} _ = pure $ actionDoc { nodeType } []
cpt { action: Download, id, nodeType, session} _ = pure $ actionDownload { id, nodeType, session } []
cpt { action: Upload, dispatch, id, nodeType, session} _ = pure $ actionUpload { dispatch, id, nodeType, session } []
cpt { action: Delete, nodeType, dispatch} _ = pure $ actionDelete { dispatch, nodeType } []
cpt { action: ManageTeam, nodeType, id, session} _ = pure $ actionManageTeam { id, nodeType, session } []
cpt { action: Add xs, dispatch, id, name, nodeType} _ =
pure $ addNodeView {dispatch, id, name, nodeType, nodeTypes: xs} []
cpt { action: Refresh , dispatch, nodeType } _ = pure $ update { dispatch, nodeType } []
cpt { action: ReloadWithSettings , dispatch, nodeType } _ = pure $ update { dispatch, nodeType } []
cpt { action : AddingContact, dispatch, id } _ =
pure $ Contact.actionAddContact { dispatch, id } []
cpt { action: Config, nodeType } _ =
pure $ fragmentPT $ "Config " <> show nodeType
-- Functions using SubTree
cpt { action: Reconstruct , dispatch, nodeType } _ = pure $ update { dispatch, nodeType } []
cpt { action: Delete, nodeType, dispatch} _ =
pure $ actionDelete { dispatch, nodeType } []
cpt { action: Documentation nodeType} _ =
pure $ actionDoc { nodeType } []
cpt { action: Download, id, nodeType, session} _ =
pure $ actionDownload { id, nodeType, session } []
cpt { action: Link {subTreeParams}, boxes, dispatch, id, nodeType, session } _ =
pure $ linkNode { boxes, dispatch, id, nodeType, session, subTreeParams } []
cpt { action: ManageTeam, nodeType, id, session} _ =
pure $ actionManageTeam { id, nodeType, session } []
cpt { action: Merge {subTreeParams}, boxes, dispatch, id, nodeType, session } _ =
pure $ mergeNode { boxes, dispatch, id, nodeType, session, subTreeParams } []
cpt { action: Move {subTreeParams}, boxes, dispatch, id, nodeType, session } _ =
pure $ moveNode { boxes, dispatch, id, nodeType, session, subTreeParams } []
cpt { action: Link {subTreeParams}, boxes, dispatch, id, nodeType, session } _ =
pure $ linkNode { boxes, dispatch, id, nodeType, session, subTreeParams } []
cpt { action : Share, dispatch, id, session } _ = pure $ Share.shareNode { dispatch, id, session } []
cpt { action : AddingContact, dispatch, id } _ = pure $ Contact.actionAddContact { dispatch, id } []
cpt { action : Publish {subTreeParams}, boxes, dispatch, id, nodeType, session } _ =
pure $ Share.publishNode { boxes, dispatch, id, nodeType, session, subTreeParams } []
cpt { action: Reconstruct , dispatch, nodeType } _ =
pure $ update { dispatch, nodeType } []
cpt { action: Refresh , dispatch, nodeType } _ =
pure $ update { dispatch, nodeType } []
cpt { action: ReloadWithSettings , dispatch, nodeType } _ =
pure $ update { dispatch, nodeType } []
cpt { action: SearchBox, boxes, dispatch, id, session } _ =
pure $ actionSearch { boxes, dispatch, id: Just id, session } []
cpt { action : Share, id, session } _ = pure $ Share.shareNode { id, session } []
cpt { action: Upload, dispatch, id, nodeType, session} _ =
pure $ actionUpload { dispatch, id, nodeType, session } []
cpt { action: WriteNodesDocuments, boxes, dispatch, id, session } _ =
pure $ actionWriteNodesDocuments { boxes, dispatch, id, session } []
cpt _ _ = pure $ H.div {} []
......@@ -2,10 +2,12 @@ module Gargantext.Components.Forest.Tree.Node.Tools where
import Gargantext.Prelude
import Data.Maybe (fromMaybe)
import Data.Array as A
import Data.Maybe (fromMaybe, Maybe(..))
import Data.Set (Set)
import Data.Set as Set
import Data.String as S
import Data.Tuple.Nested ((/\))
import Effect (Effect)
import Effect.Aff (Aff, launchAff, launchAff_)
import Gargantext.Components.Bootstrap as B
......@@ -19,6 +21,7 @@ import Gargantext.Utils.Reactix as R2
import Gargantext.Utils.Show as GUS
import Reactix as R
import Reactix.DOM.HTML as H
import Record.Extra as RX
import Toestand as T
here :: R2.Here
......@@ -27,20 +30,86 @@ here = R2.here "Gargantext.Components.Forest.Tree.Node.Tools"
fragmentPT :: String -> R.Element
fragmentPT text = H.div { style: { margin: "10px" }} [ H.text text ]
type Body = Array R.Element
type Footer = R.Element
type PanelProps =
( mError :: Maybe String )
-- | Last element of panel's children goes to footer, all others go to body
panel :: R2.Component PanelProps
panel = R.createElement panelCpt
panelCpt :: R.Component PanelProps
panelCpt = here.component "panel" cpt where
cpt { mError } children = do
let errorCpt =
R2.fromMaybe mError $
\err ->
R2.row
[ R2.col 12
[ H.div { className: "alert alert-danger" } [ H.text err ]
]
]
pure $ R.fragment
[ H.div { className: "card-body" }
[ H.div { className: "card-text" }
[ R2.row
-- TODO add type for text or form here [ H.form {className: "form-horizontal"} bodies ]
[ R2.col 12 bodies ]
, errorCpt
]
]
, H.div {className: "card-footer"}
[ R2.row
[ H.div { className: "mx-auto"} [ footer ] ]
]
]
where
bodies /\ footer =
case A.unsnoc children of
Nothing -> [] /\ (H.div {} [])
Just { init, last } -> init /\ last
-- | A panel without a footer
panelNoFooter :: R2.Component PanelProps
panelNoFooter = R.createElement panelNoFooterCpt
panelNoFooterCpt :: R.Component PanelProps
panelNoFooterCpt = here.component "panelNoFooter" cpt where
cpt props children =
pure $ panel props (children <> [ H.div {} [] ])
type PanelWithSubmitButtonProps =
( action :: Action
, dispatch :: Action -> Aff Unit
| PanelProps )
-- | A panel with 'submitButton { action, dispatch }'
panelWithSubmitButton :: R2.Component PanelWithSubmitButtonProps
panelWithSubmitButton = R.createElement panelWithSubmitButtonCpt
panelWithSubmitButtonCpt :: R.Component PanelWithSubmitButtonProps
panelWithSubmitButtonCpt = here.component "panelWithSubmitButton" cpt where
cpt props@{ action, dispatch } children = do
let pProps = (RX.pick props :: Record PanelProps)
pure $ panel pProps (children
-- footer
<> [ submitButton { action, dispatch } ])
type PanelWithSubmitButtonHrefProps =
( action :: Action
, href :: String
| PanelProps )
-- | A panel with 'submitButtonHref { action, href }'
panelWithSubmitButtonHref :: R2.Component PanelWithSubmitButtonHrefProps
panelWithSubmitButtonHref = R.createElement panelWithSubmitButtonHrefCpt
panelWithSubmitButtonHrefCpt :: R.Component PanelWithSubmitButtonHrefProps
panelWithSubmitButtonHrefCpt = here.component "panelWithSubmitButtonHref" cpt where
cpt props@{ action, href } children = do
let pProps = (RX.pick props :: Record PanelProps)
pure $ panel pProps (children
-- footer
<> [ submitButtonHref { action, href } ])
panel :: Body -> Footer -> R.Element
panel bodies submit =
R.fragment
[ H.div { className: "card-body" }
[ H.div { className: "row" }
-- TODO add type for text or form here [ H.form {className: "form-horizontal"} bodies ]
[ H.div { className: "col-12" } bodies ]]
, H.div {className: "card-footer"}
[ H.div { className: "row" }
[ H.div { className: "mx-auto"} [ submit ] ]]]
type TextInputBoxProps =
( id :: GT.ID
......@@ -163,15 +232,15 @@ inviteInputBoxCpt = here.component "textInputBox" cpt where
T.write_ ("Invited " <> R.readRef ref <> " to the team") username
launchAff_ $ dispatch (boxAction $ R.readRef ref)
type DefaultText = String
-- type DefaultText = String
formEdit :: forall prev next
. DefaultText -> ((prev -> String) -> Effect next) -> R.Element
formEdit defaultValue setter =
H.div { className: "form-group" }
[ H.input { defaultValue, type: "text", on: { input }
, placeholder: defaultValue, className: "form-control" }
] where input = setter <<< const <<< R.unsafeEventValue
-- formEdit :: forall prev next
-- . DefaultText -> ((prev -> String) -> Effect next) -> R.Element
-- formEdit defaultValue setter =
-- H.div { className: "form-group" }
-- [ H.input { defaultValue, type: "text", on: { input }
-- , placeholder: defaultValue, className: "form-control" }
-- ] where input = setter <<< const <<< R.unsafeEventValue
type FormChoiceSafeProps item m =
( items :: Array item
......@@ -252,24 +321,41 @@ formButtonCpt = here.component "formButton" cpt where
------------------------------------------------------------------------
------------------------------------------------------------------------
submitButton :: Action -> (Action -> Aff Unit) -> R.Element
submitButton action dispatch =
H.button { className : "btn btn-primary fa fa-" <> icon action
, type: "button"
, id: S.toLower $ show action
, title: show action
, on: {click: \_ -> launchAff $ dispatch action}
}
[ H.span {className: "font-family-theme mx-1"} [ H.text $ " " <> text action] ]
type SubmitButtonProps =
( action :: Action
, dispatch :: Action -> Aff Unit )
submitButton :: R2.Leaf SubmitButtonProps
submitButton = R2.leaf submitButtonCpt
submitButtonCpt :: R.Component SubmitButtonProps
submitButtonCpt = here.component "submitButton" cpt where
cpt { action
, dispatch } _ = do
pure $ H.button { className : "btn btn-primary fa fa-" <> icon action
, type: "button"
, id: S.toLower $ show action
, title: show action
, on: {click: \_ -> launchAff $ dispatch action}
}
[ H.span {className: "font-family-theme mx-1"} [ H.text $ " " <> text action] ]
type Href = String
submitButtonHref :: Action -> Href -> R.Element
submitButtonHref action href =
H.a { className, href, target: "_blank" }
[ H.span {className: "font-family-theme mx-1"} [ H.text $ " " <> text action ] ]
where
className = "btn btn-primary fa fa-" <> icon action
type SubmitButtonHrefProps =
( action :: Action
, href :: Href )
submitButtonHref :: R2.Leaf SubmitButtonHrefProps
submitButtonHref = R2.leaf submitButtonHrefCpt
submitButtonHrefCpt :: R.Component SubmitButtonHrefProps
submitButtonHrefCpt = here.component "submitButtonHref" cpt where
cpt { action, href } _ = do
pure $
H.a { className, href, target: "_blank" }
[ H.span {className: "font-family-theme mx-1"} [ H.text $ " " <> text action ] ]
where
className = "btn btn-primary fa fa-" <> icon action
------------------------------------------------------------------------
-- | CheckBox tools
......
......@@ -61,7 +61,8 @@ nodeSearchControlCpt = here.component "nodeSearchControl" cpt
}
[
inputWithAutocomplete
{ autocompleteSearch: autocompleteSearch graph
{ autoFocus: true
, autocompleteSearch: autocompleteSearch graph
, onAutocompleteClick: doSearch
, onEnterPress: doSearch
, classes: "filter-results-completions rounded-circle-2 text-small py-0"
......@@ -69,7 +70,7 @@ nodeSearchControlCpt = here.component "nodeSearchControl" cpt
, pattern: ".*"
, title: ""
, placeholder: "find and select a term here..."
}
} []
,
B.button
{ callback: \_ -> doSearch search'
......
......@@ -27,7 +27,8 @@ type Completions = Array String
type Props =
(
autocompleteSearch :: String -> Effect Completions
autoFocus :: Boolean
, autocompleteSearch :: String -> Effect Completions
, classes :: String
, onAutocompleteClick :: String -> Effect Unit
, onEnterPress :: String -> Effect Unit
......@@ -37,19 +38,20 @@ type Props =
, state :: T.Box String
)
inputWithAutocomplete :: R2.Leaf Props
inputWithAutocomplete = R2.leaf inputWithAutocompleteCpt
inputWithAutocomplete :: R2.Component Props
inputWithAutocomplete = R2.component inputWithAutocompleteCpt
inputWithAutocompleteCpt :: R.Component Props
inputWithAutocompleteCpt = here.component "inputWithAutocomplete" cpt
where
cpt { autocompleteSearch
cpt { autoFocus
, autocompleteSearch
, classes
, onAutocompleteClick
, onEnterPress
, placeholder
, pattern
, title
, state } _ = do
, state } children = do
-- States
state' <- T.useLive T.unequal state
containerRef <- R.useRef null
......@@ -67,14 +69,15 @@ inputWithAutocompleteCpt = here.component "inputWithAutocomplete" cpt
{ className: "input-with-autocomplete " <> classes
, ref: containerRef
}
[
([
completionsCpt { completions, onAutocompleteClick, state } []
, H.input { type: "text"
, ref: inputRef
, autoFocus
, className: "form-control"
, value: state'
, pattern: pattern
, title: title
, pattern
, title
, placeholder
, on: { focus: onFocus completions state'
, input: onInput completions
......@@ -83,7 +86,7 @@ inputWithAutocompleteCpt = here.component "inputWithAutocomplete" cpt
, blur: onBlur completions containerRef
}
}
]
] <> children)
-- Helpers
where
......@@ -143,139 +146,6 @@ inputWithAutocompleteCpt = here.component "inputWithAutocomplete" cpt
else
pure $ false
type Props' =
(
autocompleteSearch :: String -> Effect Completions
, classes :: String
, onAutocompleteClick :: String -> Effect Unit
, dispatch :: Action -> Aff Unit
, boxAction :: String -> Action
, state :: T.Box String
, text :: T.Box String
, pattern :: String
, title :: String
, placeholder :: String
)
inputWithAutocomplete' :: R2.Leaf Props'
inputWithAutocomplete' = R2.leaf inputWithAutocompleteCpt'
inputWithAutocompleteCpt' :: R.Component Props'
inputWithAutocompleteCpt' = here.component "inputWithAutocomplete" cpt
where
cpt { autocompleteSearch
, classes
, onAutocompleteClick
, dispatch
, boxAction
, state
, text
, pattern
, title
, placeholder } _ = do
-- States
state' <- T.useLive T.unequal state
containerRef <- R.useRef null
inputRef <- R.useRef null
completions <- T.useBox []
R.useEffectOnce' $ do
cs <- autocompleteSearch state'
T.write_ cs completions
-- Render
pure $
H.div
{ className: "input-with-autocomplete " <> classes
, ref: containerRef
}
[
completionsCpt { completions, onAutocompleteClick, state } []
, H.input { type: "text"
, ref: inputRef
, className: "form-control"
, value: state'
, pattern: pattern
, title: title
, placeholder
, on: { focus: onFocus completions state'
, input: onInput completions
, change: onInput completions
, keyUp: onInputKeyUp inputRef
, blur: onBlur completions containerRef
}
}
, B.iconButton
{ callback: submit state'
, title: "Submit"
, name: "send"
, elevation: Level1
}
]
-- Helpers
where
-- (!) `onBlur` DOM.Event is triggered before any `onClick` DOM.Event
-- So when a completion is being clicked, the UX will be broken
--
-- ↳ As a solution we chose to check if the click is made from
-- the autocompletion list
onBlur :: forall event.
T.Box Completions
-> R.Ref (Nullable DOM.Element)
-> event
-> Effect Unit
onBlur completions containerRef event =
if isInnerEvent
then
pure $ (event .. "preventDefault")
else
T.write_ [] completions
where
mContains = do
a <- toMaybe $ R.readRef containerRef
b <- toMaybe (event .. "relatedTarget")
Just (contains a b)
isInnerEvent = maybe false identity mContains
onFocus :: forall event. T.Box Completions -> String -> event -> Effect Unit
onFocus completions st _ = do
cs <- autocompleteSearch st
T.write_ cs completions
onInput :: forall event. T.Box Completions -> event -> Effect Unit
onInput completions e = do
let val = R.unsafeEventValue e
T.write_ val state
cs <- autocompleteSearch val
T.write_ cs completions
onInputKeyUp :: R.Ref (Nullable DOM.Element) -> DE.KeyboardEvent -> Effect Boolean
onInputKeyUp inputRef e = do
if DE.key e == "Enter" then do
R2.preventDefault e
R2.stopPropagation e
let val = S.trim $ R.unsafeEventValue e
let mInput = toMaybe $ R.readRef inputRef
T.write_ val state
launchAff_ $ dispatch (boxAction val)
T.write_ ("Invited " <> val <> " to the team") text
case mInput of
Nothing -> pure false
Just input -> do
R2.blur input
pure false
else
pure $ false
submit val _ = do
T.write_ ("Invited " <> S.trim val <> " to the team") text
launchAff_ $ dispatch (boxAction val)
---------------------------------------------------------
type CompletionsProps =
......
......@@ -139,7 +139,8 @@ component = here.component "main" cpt where
}
[
inputWithAutocomplete
{ autocompleteSearch: searchCallback
{ autoFocus: true
, autocompleteSearch: searchCallback
, onAutocompleteClick: autocompleteClickCallback
, onEnterPress: \s -> do
cs <- searchCallback s
......@@ -151,7 +152,7 @@ component = here.component "main" cpt where
, title: ""
, placeholder: "find and select a term here..."
, state: searchState
}
} []
]
]
......
......@@ -78,15 +78,15 @@ treeSearchCpt = here.component "treeSearch" cpt where
{ className: "input-group p-1" }
[
inputWithEnter { className: "form-control"
, autoFocus: true
, onEnter: submit inputRef query
, onValueChanged: R.setRef inputRef
, onBlur: R.setRef inputRef
, type: "value"
, defaultValue: ""
, required: true
, placeholder: "Search keys..."
}
, autoFocus: true
, onEnter: submit inputRef query
, onValueChanged: R.setRef inputRef
, onBlur: R.setRef inputRef
, type: "value"
, defaultValue: ""
, required: true
, placeholder: "Search keys..."
}
,
H.div { className: "input-group-append"}
[
......
This diff is collapsed.
......@@ -115,12 +115,9 @@ postAuthRequest :: Backend -> AuthRequest -> Aff (Either String Session)
postAuthRequest backend ar@(AuthRequest {username}) =
decode <$> REST.post Nothing (toUrl backend "auth") ar
where
decode (Left err) = Left $ "Error when sending REST.post: " <> show err
decode (Right (AuthResponse ar2))
| {inval: Just (AuthInvalid {message})} <- ar2 = Left message
| {valid: Just (AuthData {token, tree_id, user_id})} <- ar2 =
Right $ Session { backend, caches: Map.empty, token, treeId: tree_id, username, userId: user_id }
| otherwise = Left "Invalid response from server"
decode (Left err) = Left $ show err
decode (Right (AuthData { token, tree_id, user_id })) =
Right $ Session { backend, caches: Map.empty, token, treeId: tree_id, username, userId: user_id }
postForgotPasswordRequest :: Backend -> String -> Aff (Either String { status :: String })
postForgotPasswordRequest backend email =
......
......@@ -147,7 +147,7 @@ newtype Point = Point { x :: Number, y :: Number }
-- a reducer function living in effector, for useReductor
type Actor s a = (a -> s -> Effect s)
-- | Turns a ReactElement into aReactix Element
-- | Turns a ReactElement into a Reactix Element
-- | buff (v.) to polish
buff :: ReactElement -> R.Element
buff = unsafeCoerce
......
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