Contacts.purs 11.5 KB
Newer Older
1 2
module Gargantext.Components.Nodes.Annuaire.User.Contacts
  ( module Gargantext.Components.Nodes.Annuaire.User.Contacts.Types
3
  , annuaireUserLayout
4 5
  , userLayout )
  where
6

7
import DOM.Simple.Console (log2)
8
import Data.Lens as L
9
import Data.Maybe (Maybe(..), fromMaybe)
10
import Data.Tuple (Tuple(..), fst, snd)
11
import Data.Tuple.Nested ((/\))
12
import Effect (Effect)
13
import Effect.Aff (Aff, launchAff_)
14
import Effect.Class (liftEffect)
15 16 17
import Reactix as R
import Reactix.DOM.HTML as H

18
import Gargantext.AsyncTasks as GAT
19
import Gargantext.Components.InputWithEnter (inputWithEnter)
20
import Gargantext.Components.Nodes.Annuaire.User.Contacts.Tabs as Tabs
21
import Gargantext.Components.Nodes.Annuaire.User.Contacts.Types (Contact(..), ContactData, ContactTouch(..), ContactWhere(..), ContactWho(..), HyperdataContact(..), HyperdataUser(..), _city, _country, _firstName, _labTeamDeptsJoinComma, _lastName, _mail, _office, _organizationJoinComma, _ouFirst, _phone, _role, _shared, _touch, _who, defaultContactTouch, defaultContactWhere, defaultContactWho, defaultHyperdataContact, defaultHyperdataUser)
22
import Gargantext.Components.Nodes.Lists.Types as LT
23
import Gargantext.Ends (Frontends)
24
import Gargantext.Hooks.Loader (useLoader)
25
import Gargantext.Prelude (Unit, bind, const, discard, pure, show, unit, ($), (+), (<$>), (<<<), (<>), (==))
26
import Gargantext.Routes as Routes
27
import Gargantext.Sessions (Session, get, put, sessionId)
28
import Gargantext.Types (NodeType(..), ReloadS)
29
import Gargantext.Utils.Reactix as R2
30

31
thisModule :: String
32
thisModule = "Gargantext.Components.Nodes.Annuaire.User.Contacts"
33

34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57
type DisplayProps = (
  title :: String
  )

display :: R2.Component DisplayProps
display = R.createElement displayCpt

displayCpt :: R.Component DisplayProps
displayCpt = R.hooksComponentWithModule thisModule "display" cpt
  where
    cpt { title } children = do
      pure $ H.div { className: "container-fluid" }
        [ H.div { className: "row", id: "contact-page-header" }
          [ H.div { className: "col-md-6"} [ H.h3 {} [ H.text title ] ]
          , H.div { className: "col-md-8"} []
          , H.div { className: "col-md-2"} [ H.span {} [ H.text "" ] ]
          ]
        , H.div { className: "row", id: "contact-page-info" }
          [ H.div { className: "col-md-12" }
            [ H.div { className: "row" }
              [ H.div { className: "col-md-2" } [ H.img { src: "/images/Gargantextuel-212x300.jpg"} ]
              , H.div { className: "col-md-1"} []
              , H.div { className: "col-md-8"} children
              ]]]]
58 59

-- | TODO format data in better design (UI) shape
60 61
contactInfos :: HyperdataUser -> (HyperdataUser -> Effect Unit) -> Array R.Element
contactInfos h onUpdateHyperdata = item <$> contactInfoItems
62
  where
63
    item {label, defaultVal, lens} =
64 65 66
      contactInfoItem { hyperdata: h
                      , label
                      , lens
67 68
                      , onUpdateHyperdata
                      , placeholder: defaultVal }
69

70
contactInfoItems :: Array {label:: String, defaultVal:: String, lens:: HyperdataUserLens}
71
contactInfoItems =
72 73 74
  [ {label: "Last Name"    , defaultVal: "Empty Last Name"    , lens: _shared <<< _who     <<< _lastName             }
  , {label: "First Name"   , defaultVal: "Empty First Name"   , lens: _shared <<< _who     <<< _firstName            }
  , {label: "Organisation" , defaultVal: "Empty Organisation" , lens: _shared <<< _ouFirst <<< _organizationJoinComma}
75
  , {label: "Lab/Team/Dept", defaultVal: "Empty Lab/Team/Dept", lens: _shared <<< _ouFirst <<< _labTeamDeptsJoinComma}
76 77 78 79 80 81 82
  , {label: "Office"       , defaultVal: "Empty Office"       , lens: _shared <<< _ouFirst <<< _office               }
  , {label: "City"         , defaultVal: "Empty City"         , lens: _shared <<< _ouFirst <<< _city                 }
  , {label: "Country"      , defaultVal: "Empty Country"      , lens: _shared <<< _ouFirst <<< _country              }
  , {label: "Role"         , defaultVal: "Empty Role"         , lens: _shared <<< _ouFirst <<< _role                 }
  , {label: "Phone"        , defaultVal: "Empty Phone"        , lens: _shared <<< _ouFirst <<< _touch <<< _phone     }
  , {label: "Mail"         , defaultVal: "Empty Mail"         , lens: _shared <<< _ouFirst <<< _touch <<< _mail      }
  ]
83

84
type HyperdataUserLens = L.ALens' HyperdataUser String
85 86

type ContactInfoItemProps =
87
  ( hyperdata :: HyperdataUser
88
  , label :: String
89 90
  , lens :: HyperdataUserLens
  , onUpdateHyperdata :: HyperdataUser -> Effect Unit
91
  , placeholder :: String
92 93 94 95 96 97
  )

contactInfoItem :: Record ContactInfoItemProps -> R.Element
contactInfoItem props = R.createElement contactInfoItemCpt props []

contactInfoItemCpt :: R.Component ContactInfoItemProps
98
contactInfoItemCpt = R.hooksComponentWithModule thisModule "contactInfoItem" cpt
99
  where
100
    cpt {hyperdata, label, lens, onUpdateHyperdata, placeholder} _ = do
101
      isEditing <- R.useState' false
102
      let value = (L.view cLens hyperdata) :: String
103 104
      valueRef <- R.useRef value

105 106 107
      pure $ H.div { className: "form-group row" } [
        H.span { className: "col-sm-2 col-form-label" } [ H.text label ]
      , item isEditing valueRef
108
      ]
109

110
      where
111
        cLens = L.cloneLens lens
112
        item (false /\ setIsEditing) valueRef =
113 114 115 116 117 118 119 120 121
          H.div { className: "input-group col-sm-6" } [
            H.input { className: "form-control"
                    , defaultValue: placeholder
                    , disabled: 1
                    , type: "text" }
          , H.div { className: "btn input-group-append"
                  , on: { click: onClick } } [
              H.div { className: "input-group-text fa fa-pencil" } []
            ]
122 123
          ]
          where
124
            placeholder = R.readRef valueRef
125 126
            onClick _ = setIsEditing $ const true
        item (true /\ setIsEditing) valueRef =
127 128 129 130 131 132 133 134 135 136 137 138 139 140
          H.div { className: "input-group col-sm-6" } [
            inputWithEnter {
                autoFocus: true
              , className: "form-control"
              , defaultValue: R.readRef valueRef
              , onEnter: onClick
              , onValueChanged: R.setRef valueRef
              , placeholder
              , type: "text"
              }
          , H.div { className: "btn input-group-append"
                  , on: { click: onClick } } [
              H.div { className: "input-group-text fa fa-floppy-o" } []
            ]
141 142 143 144
          ]
          where
            onClick _ = do
              setIsEditing $ const false
145 146
              let newHyperdata = (L.over cLens (\_ -> R.readRef valueRef) hyperdata) :: HyperdataUser
              onUpdateHyperdata newHyperdata
147

148 149 150
listElement :: Array R.Element -> R.Element
listElement = H.li { className: "list-group-item justify-content-between" }

151
type LayoutProps = (
152
    appReload     :: ReloadS
153
  , asyncTasksRef :: R.Ref (Maybe GAT.Reductor)
154 155 156
  , frontends     :: Frontends
  , nodeId        :: Int
  , session       :: Session
157
  , treeReloadRef :: R.Ref (Maybe ReloadS)
158
  )
159

160 161 162 163
type KeyLayoutProps = (
    key :: String
  | LayoutProps
  )
164 165 166 167 168

userLayout :: Record LayoutProps -> R.Element
userLayout props = R.createElement userLayoutCpt props []

userLayoutCpt :: R.Component LayoutProps
169
userLayoutCpt = R.hooksComponentWithModule thisModule "userLayout" cpt
170
  where
171
    cpt { appReload, asyncTasksRef, frontends, nodeId, session, treeReloadRef } _ = do
172 173
      let sid = sessionId session

174 175 176 177 178 179 180
      pure $ userLayoutWithKey {
          appReload
        , asyncTasksRef
        , frontends
        , key: show sid <> "-" <> show nodeId
        , nodeId
        , session
181
        , treeReloadRef
182
        }
183 184 185 186 187

userLayoutWithKey :: Record KeyLayoutProps -> R.Element
userLayoutWithKey props = R.createElement userLayoutWithKeyCpt props []

userLayoutWithKeyCpt :: R.Component KeyLayoutProps
188
userLayoutWithKeyCpt = R.hooksComponentWithModule thisModule "userLayoutWithKey" cpt
189
  where
190
    cpt { appReload, asyncTasksRef, frontends, nodeId, session, treeReloadRef } _ = do
191 192
      reload <- R.useState' 0

193
      cacheState <- R.useState' LT.CacheOn
194

195
      sidePanelTriggers <- LT.emptySidePanelTriggers
196

197
      useLoader {nodeId, reload: fst reload, session} getContactWithReload $
198
        \contactData@{contactNode: Contact {name, hyperdata}} ->
199
          H.ul { className: "col-md-12 list-group" } [
200
            display { title: fromMaybe "no name" name } (contactInfos hyperdata (onUpdateHyperdata reload))
201 202 203 204 205 206 207 208
          , Tabs.tabs {
                 appReload
               , asyncTasksRef
               , cacheState
               , contactData
               , frontends
               , nodeId
               , session
209
               , sidePanelTriggers
210
               , treeReloadRef
211
               }
212
          ]
213
      where
214
        onUpdateHyperdata :: ReloadS -> HyperdataUser -> Effect Unit
215
        onUpdateHyperdata (_ /\ setReload) hd = do
216
          launchAff_ $ do
217 218
            _ <- saveContactHyperdata session nodeId hd
            liftEffect $ setReload $ (+) 1
219

220
-- | toUrl to get data
221 222
getContact :: Session -> Int -> Aff ContactData
getContact session id = do
223
  contactNode <- get session $ Routes.NodeAPI Node (Just id) ""
224 225 226 227 228 229 230 231 232
  -- TODO: we need a default list for the pairings
  --defaultListIds <- get $ toUrl endConfigStateful Back (Children NodeList 0 1 Nothing) $ Just id
  --case (head defaultListIds :: Maybe (NodePoly HyperdataList)) of
  --  Just (NodePoly { id: defaultListId }) ->
  --    pure {contactNode, defaultListId}
  --  Nothing ->
  --    throwError $ error "Missing default list"
  pure {contactNode, defaultListId: 424242}

233 234 235
getContactWithReload :: {nodeId :: Int, reload :: Int, session :: Session} -> Aff ContactData
getContactWithReload {nodeId, session} = getContact session nodeId

236 237 238 239
saveContactHyperdata :: Session -> Int -> HyperdataUser -> Aff Int
saveContactHyperdata session id h = do
  put session (Routes.NodeAPI Node (Just id) "") h

240

241 242
type AnnuaireLayoutProps = (
    annuaireId :: Int
243 244 245 246 247 248 249
  | LayoutProps )


annuaireUserLayout :: Record AnnuaireLayoutProps -> R.Element
annuaireUserLayout props = R.createElement annuaireUserLayoutCpt props []

annuaireUserLayoutCpt :: R.Component AnnuaireLayoutProps
250
annuaireUserLayoutCpt = R.hooksComponentWithModule thisModule "annuaireUserLayout" cpt
251
  where
252
    cpt { annuaireId, appReload, asyncTasksRef, frontends, nodeId, session, treeReloadRef } _ = do
253
      cacheState <- R.useState' LT.CacheOn
254

255
      sidePanelTriggers <- LT.emptySidePanelTriggers
256

257 258
      useLoader nodeId (getAnnuaireContact session annuaireId) $
        \contactData@{contactNode: Contact {name, hyperdata}} ->
259
          H.ul { className: "col-md-12 list-group" } [
260
            display { title: fromMaybe "no name" name } (contactInfos hyperdata onUpdateHyperdata)
261 262 263 264 265 266 267 268
          , Tabs.tabs {
                 appReload
               , asyncTasksRef
               , cacheState
               , contactData
               , frontends
               , nodeId
               , session
269
               , sidePanelTriggers
270
               , treeReloadRef
271 272
               }
          ]
273

274 275 276 277
      where
        onUpdateHyperdata :: HyperdataUser -> Effect Unit
        onUpdateHyperdata _ = pure unit

278 279
getAnnuaireContact :: Session -> Int -> Int -> Aff ContactData
getAnnuaireContact session annuaireId id = do
280
  contactNode <- get session $ Routes.NodeAPI Annuaire (Just annuaireId) $ "contact/" <> (show id)
281 282 283 284 285 286 287 288
  -- TODO: we need a default list for the pairings
  --defaultListIds <- get $ toUrl endConfigStateful Back (Children NodeList 0 1 Nothing) $ Just id
  --case (head defaultListIds :: Maybe (NodePoly HyperdataList)) of
  --  Just (NodePoly { id: defaultListId }) ->
  --    pure {contactNode, defaultListId}
  --  Nothing ->
  --    throwError $ error "Missing default list"
  pure {contactNode, defaultListId: 424242}