Contacts.purs 10.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 18
import Reactix as R
import Reactix.DOM.HTML as H

import Gargantext.Components.InputWithEnter (inputWithEnter)
19
import Gargantext.Components.Nodes.Annuaire.User.Contacts.Tabs as Tabs
20
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)
21
import Gargantext.Components.Nodes.Lists.Types as NT
22
import Gargantext.Ends (Frontends)
23
import Gargantext.Hooks.Loader (useLoader)
24
import Gargantext.Prelude (Unit, bind, const, discard, pure, show, unit, ($), (+), (<$>), (<<<), (<>), (==))
25
import Gargantext.Routes as Routes
26
import Gargantext.Sessions (Session, get, put, sessionId)
27
import Gargantext.Types (NodeType(..))
28 29 30
import Gargantext.Utils.Reactix as R2

thisModule = "Gargantext.Components.Nodes.Annuaire.User.Contacts"
31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48

display :: String -> Array R.Element -> R.Element
display title elems =
  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"} elems
        ]]]]

-- | TODO format data in better design (UI) shape
49 50
contactInfos :: HyperdataUser -> (HyperdataUser -> Effect Unit) -> Array R.Element
contactInfos h onUpdateHyperdata = item <$> contactInfoItems
51
  where
52
    item {label, defaultVal, lens} =
53 54 55
      contactInfoItem { hyperdata: h
                      , label
                      , lens
56 57
                      , onUpdateHyperdata
                      , placeholder: defaultVal }
58

59
contactInfoItems :: Array {label:: String, defaultVal:: String, lens:: HyperdataUserLens}
60
contactInfoItems =
61 62 63
  [ {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}
64
  , {label: "Lab/Team/Dept", defaultVal: "Empty Lab/Team/Dept", lens: _shared <<< _ouFirst <<< _labTeamDeptsJoinComma}
65 66 67 68 69 70 71
  , {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      }
  ]
72

73
type HyperdataUserLens = L.ALens' HyperdataUser String
74 75

type ContactInfoItemProps =
76
  ( hyperdata :: HyperdataUser
77
  , label :: String
78 79
  , lens :: HyperdataUserLens
  , onUpdateHyperdata :: HyperdataUser -> Effect Unit
80
  , placeholder :: String
81 82 83 84 85 86
  )

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

contactInfoItemCpt :: R.Component ContactInfoItemProps
87
contactInfoItemCpt = R.hooksComponentWithModule thisModule "contactInfoItem" cpt
88
  where
89
    cpt {hyperdata, label, lens, onUpdateHyperdata, placeholder} _ = do
90
      isEditing <- R.useState' false
91
      let value = (L.view cLens hyperdata) :: String
92 93 94
      valueRef <- R.useRef value

      pure $ H.li { className: "list-group-item" } [
95 96
          H.span { className: "badge badge-default badge-pill"} [ H.text label ]
        , item isEditing valueRef
97 98
      ]
      where
99
        cLens = L.cloneLens lens
100 101 102 103 104
        usePlaceholder valueRef =
          if R.readRef valueRef == "" then
            Tuple true placeholder
          else
            Tuple false $ R.readRef valueRef
105 106
        item (false /\ setIsEditing) valueRef =
          H.span {} [
107 108 109
              H.span { className: if (fst $ usePlaceholder valueRef) then "text-muted" else "" } [
                H.text $ snd $ usePlaceholder valueRef
              ]
110 111 112 113 114 115 116
            , H.span { className: "fa fa-pencil"
                     , on: {click: onClick} } []
          ]
          where
            onClick _ = setIsEditing $ const true
        item (true /\ setIsEditing) valueRef =
          H.span {} [
117 118 119 120 121 122 123 124 125
              inputWithEnter {
                  onEnter: onClick
                , onValueChanged: R.setRef valueRef
                , autoFocus: true
                , className: "form-control"
                , defaultValue: R.readRef valueRef
                , placeholder
                , type: "text"
                }
126 127 128 129 130 131
            , H.span { className: "fa fa-floppy-o"
                     , on: {click: onClick} } []
          ]
          where
            onClick _ = do
              setIsEditing $ const false
132 133
              let newHyperdata = (L.over cLens (\_ -> R.readRef valueRef) hyperdata) :: HyperdataUser
              onUpdateHyperdata newHyperdata
134

135 136 137 138 139 140 141 142 143 144 145
listInfo :: Tuple String String -> R.Element
listInfo s = listElement $ infoRender s

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

infoRender :: Tuple String String -> Array R.Element
infoRender (Tuple title content) =
  [ H.span { className: "badge badge-default badge-pill"} [ H.text title ]
  , H.span {} [H.text content] ]

146 147 148 149 150
type LayoutProps = (
    frontends :: Frontends
  , nodeId :: Int
  , session :: Session
  )
151

152 153 154 155
type KeyLayoutProps = (
    key :: String
  | LayoutProps
  )
156 157 158 159 160

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

userLayoutCpt :: R.Component LayoutProps
161
userLayoutCpt = R.hooksComponentWithModule thisModule "userLayout" cpt
162 163 164 165 166 167 168 169 170 171
  where
    cpt { frontends, nodeId, session } _ = do
      let sid = sessionId session

      pure $ userLayoutWithKey { frontends, key: show sid <> "-" <> show nodeId, nodeId, session }

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

userLayoutWithKeyCpt :: R.Component KeyLayoutProps
172
userLayoutWithKeyCpt = R.hooksComponentWithModule thisModule "userLayoutWithKey" cpt
173
  where
174
    cpt { frontends, nodeId, session } _ = do
175 176
      reload <- R.useState' 0

177 178
      cacheState <- R.useState' NT.CacheOn

179
      useLoader {nodeId, reload: fst reload, session} getContactWithReload $
180
        \contactData@{contactNode: Contact {name, hyperdata}} ->
181 182 183 184
          H.ul { className: "col-md-12 list-group" } [
            display (fromMaybe "no name" name) (contactInfos hyperdata (onUpdateHyperdata reload))
          , Tabs.tabs { cacheState, contactData, frontends, nodeId, session }
          ]
185
      where
186 187
        onUpdateHyperdata :: R.State Int -> HyperdataUser -> Effect Unit
        onUpdateHyperdata (_ /\ setReload) hd = do
188
          log2 "[onUpdateHyperdata] hd" hd
189
          launchAff_ $ do
190 191
            _ <- saveContactHyperdata session nodeId hd
            liftEffect $ setReload $ (+) 1
192

193
-- | toUrl to get data
194 195
getContact :: Session -> Int -> Aff ContactData
getContact session id = do
196
  contactNode <- get session $ Routes.NodeAPI Node (Just id) ""
197 198 199 200 201 202 203 204 205
  -- 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}

206 207 208
getContactWithReload :: {nodeId :: Int, reload :: Int, session :: Session} -> Aff ContactData
getContactWithReload {nodeId, session} = getContact session nodeId

209 210 211 212
saveContactHyperdata :: Session -> Int -> HyperdataUser -> Aff Int
saveContactHyperdata session id h = do
  put session (Routes.NodeAPI Node (Just id) "") h

213 214 215 216 217 218 219 220 221 222

type AnnuaireLayoutProps =
  ( annuaireId :: Int
  | LayoutProps )


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

annuaireUserLayoutCpt :: R.Component AnnuaireLayoutProps
223
annuaireUserLayoutCpt = R.hooksComponentWithModule thisModule "annuaireUserLayout" cpt
224
  where
225 226 227
    cpt { annuaireId, frontends, nodeId, session } _ = do
      cacheState <- R.useState' NT.CacheOn

228 229 230
      useLoader nodeId (getAnnuaireContact session annuaireId) $
        \contactData@{contactNode: Contact {name, hyperdata}} ->
          H.ul { className: "col-md-12 list-group" }
231
          [ display (fromMaybe "no name" name) (contactInfos hyperdata onUpdateHyperdata)
232
          , Tabs.tabs { cacheState, contactData, frontends, nodeId, session } ]
233

234 235 236 237
      where
        onUpdateHyperdata :: HyperdataUser -> Effect Unit
        onUpdateHyperdata _ = pure unit

238 239
getAnnuaireContact :: Session -> Int -> Int -> Aff ContactData
getAnnuaireContact session annuaireId id = do
240
  contactNode <- get session $ Routes.NodeAPI Annuaire (Just annuaireId) $ "contact/" <> (show id)
241 242 243 244 245 246 247 248
  -- 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}