SearchField.purs 13.9 KB
Newer Older
1
module Gargantext.Components.Forest.Tree.Node.Action.Search.SearchField where
2

3 4
import DOM.Simple.Console (log, log2)
import Data.Maybe (Maybe(..), maybe, fromMaybe)
5
import Data.Nullable (null)
6
import Data.Newtype (over)
7
import Data.Set as Set
8
import Data.String (length)
9
import Data.Tuple.Nested ((/\))
10
import Effect (Effect)
11 12
import Effect.Aff (launchAff_)
import Effect.Class (liftEffect)
13 14 15
import Reactix as R
import Reactix.DOM.HTML as H

16
import Gargantext.Components.Forest.Tree.Node.Tools (panel)
17
import Gargantext.Components.Forest.Tree.Node.Action.Search.Types (DataField(..), Database(..), IMT_org(..), Org(..), SearchQuery(..), allIMTorgs, allOrgs, dataFields, defaultSearchQuery, doc, performSearch, datafield2database, Search)
18
import Gargantext.Components.Lang (Lang)
19
import Gargantext.Prelude (Unit, bind, discard, map, pure, show, ($), (<), (<$>), (<>), (==), read)
20
import Gargantext.Sessions (Session)
21
import Gargantext.Components.Forest.Tree.Node.Action.Search.Frame (searchIframes)
22 23
import Gargantext.Types as GT
import Gargantext.Utils.Reactix as R2
24 25

thisModule = "Gargantext.Components.Forest.Tree.Node.Action.Search.SearchField"
26

27
defaultSearch :: Search
28
defaultSearch = { databases: Empty
29
                , datafield: Nothing
30 31 32
                , node_id  : Nothing
                , lang     : Nothing
                , term     : ""
33
                , url: ""
34
                }
35

36
type Props =
37 38
  -- list of databases to search, or parsers to use on uploads
  ( databases :: Array Database
39
  , langs     :: Array Lang
40
  -- State hook for a search, how we get data in and out
41
  , onSearch  :: GT.AsyncTaskWithType -> Effect Unit
42
  , search    :: R.State Search
43
  , session   :: Session
44 45 46 47 48
  )

searchField :: Record Props -> R.Element
searchField p = R.createElement searchFieldComponent p []

49
--searchFieldComponent :: R.Memo Props
50
--searchFieldComponent = R.memo (R2.hooksComponent thisModule "searchField" cpt) eqProps
51
searchFieldComponent :: R.Component Props
52
searchFieldComponent = R2.hooksComponent thisModule "searchField" cpt
53
  where
54
    cpt props@{onSearch, search: search@(s /\ _)} _ = do
55
      iframeRef <- R.useRef    null
56
      let params = 
57
                [ searchInput {search}
58
                , if length s.term < 3  -- search with love : <3
59
                  then
60
                    H.div {}[]
61
                  else
62
                    H.div {} [ dataFieldNav search dataFields
63

64 65 66 67 68 69 70 71 72 73 74 75 76 77 78
                             , if isExternal s.datafield
                                 then databaseInput search props.databases
                                 else H.div {} []
 
                             , if isHAL s.datafield
                                 then orgInput search allOrgs
                                 else H.div {} []
 
                             , if isIMT s.datafield
                                 then componentIMT search
                                 else H.div {} []
 
                             , if isCNRS s.datafield
                                 then componentCNRS search
                                 else H.div {} []
79
 
80
                             , H.div {} [ searchIframes { iframeRef, search } ]
81

82 83 84
                             , if needsLang s.datafield
                                then langNav search props.langs
                                else H.div {} []
85
                             ]
86

87
                ]
88
      let button =  submitButton {onSearch, search, session: props.session}
89

90
      pure $ panel params button
91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108


componentIMT (search /\ setSearch) =
  R.fragment
  [ H.ul {} $ map liCpt allIMTorgs
  --, filterInput fi
  ]
  where
    liCpt org =
      H.li {}
      [ H.input { type: "checkbox"
                , checked: isIn org search.datafield
                , on: { change: \_ -> ( setSearch $ _ { datafield = updateFilter org search.datafield })
                      }
                }
      , if org == All_IMT
        then H.i {} [H.text  $ " " <> show org]
        else H.text $ " " <> show org
109
      ]
110

111 112 113 114 115 116
componentCNRS (search /\ setSearch) =
  R.fragment [
    H.div {} []
  --, filterInput fi
  ]

117

118 119 120 121 122
isExternal :: Maybe DataField -> Boolean
isExternal (Just (External _)) = true
isExternal _ = false

isHAL :: Maybe DataField -> Boolean
123 124 125 126 127 128
isHAL (Just
        ( External 
          ( Just (HAL _ )
          )
        )
      ) = true
129 130
isHAL _ = false

131
isIsTex :: Maybe DataField -> Boolean
132 133 134 135 136 137
isIsTex ( Just
          ( External
            ( Just ( IsTex)
            )
          )
        ) = true
138 139
isIsTex _ = false

140

141
isIMT :: Maybe DataField -> Boolean
142 143 144 145 146 147 148 149 150 151
isIMT ( Just 
        ( External
          ( Just 
            ( HAL 
              ( Just ( IMT _)
              )
            )
          )
        )
      ) = true
152 153 154
isIMT _ = false

isCNRS :: Maybe DataField -> Boolean
155 156 157 158 159 160 161 162 163 164
isCNRS ( Just
         ( External
          ( Just
            ( HAL
              ( Just ( CNRS _)
              )
            )
          )
        )
      ) = true
165 166
isCNRS _ = false

167 168 169
needsLang :: Maybe DataField -> Boolean
needsLang (Just Gargantext) = true
needsLang (Just Web)        = true
170 171 172 173 174 175 176
needsLang ( Just
            ( External
              ( Just (HAL _)
              )
            )
          ) = true
needsLang _                 = false
177 178 179


isIn :: IMT_org -> Maybe DataField -> Boolean
180 181 182 183 184 185 186 187 188 189 190
isIn org ( Just
           ( External
             ( Just
               ( HAL
                 ( Just
                   ( IMT imtOrgs )
                 )
               )
             )
           )
         ) = Set.member org imtOrgs
191 192 193 194 195
isIn _ _ = false

updateFilter :: IMT_org -> Maybe DataField -> Maybe DataField
updateFilter org (Just (External (Just (HAL (Just (IMT imtOrgs)))))) =
 (Just (External (Just (HAL (Just $ IMT imtOrgs')))))
196 197
  where
    imtOrgs' = if Set.member org imtOrgs
198 199 200
                  then
                    if org == All_IMT
                       then Set.empty
201
                       else Set.delete All_IMT $ Set.delete org imtOrgs
202 203 204 205
                  else
                    if org == All_IMT
                       then Set.fromFoldable allIMTorgs
                       else Set.insert org imtOrgs
206

207
updateFilter org _ = (Just (External (Just (HAL (Just (IMT imtOrgs'))))))
208
  where
209 210 211
    imtOrgs' = if org == All_IMT
                  then Set.fromFoldable allIMTorgs
                  else Set.fromFoldable [org]
212

213
------------------------------------------------------------------------
214 215
langNav :: R.State Search -> Array Lang -> R.Element
langNav ({lang} /\ setSearch) langs =
216
  R.fragment [ H.div {className: "text-primary center"} [H.text "with lang"]
217
             , H.div {className: "nav nav-tabs"} (liItem <$> langs)
218 219 220
             ]
    where
      liItem :: Lang -> R.Element
221
      liItem  lang' =
222
        H.div { className : "nav-item nav-link" <> if (Just lang') == lang then " active" else ""
223 224
              , on: { click: \_ -> setSearch $ _ { lang = Just lang' } }
              } [ H.text (show lang') ]
225 226

------------------------------------------------------------------------
227 228
dataFieldNav :: R.State Search -> Array DataField -> R.Element
dataFieldNav ({datafield} /\ setSearch) datafields =
Alexandre Delanoë's avatar
Alexandre Delanoë committed
229 230 231 232 233
  R.fragment [ H.div { className: "text-primary center"} [H.text "with DataField"]
                     , H.div {className: "nav nav-tabs"} (liItem <$> dataFields)
                     , H.div {className: "center"} [ H.text
                                                   $ maybe "TODO: add Doc Instance" doc datafield 
                                                   ]
234 235 236
             ]
    where
      liItem :: DataField -> R.Element
237
      liItem  df' =
238 239 240 241 242
        H.div { className : "nav-item nav-link"
                          <> if (Just df') == datafield
                               then " active"
                               else ""
            , on: { click: \_ -> setSearch $ _ { datafield = Just df'
Alexandre Delanoë's avatar
Alexandre Delanoë committed
243
                                               , databases = datafield2database df'
244
                                               }
Alexandre Delanoë's avatar
Alexandre Delanoë committed
245
                  }
246 247
            -- just one database query for now
            -- a list a selected database needs more ergonomy
248
            } [ H.text (show df') ]
249 250

------------------------------------------------------------------------
251
{-
252 253 254 255
databaseNav  :: R.State Search
              -> Array Database
              -> R.Element
databaseNav ({datafield} /\ setSearch) dbs =
256 257 258
  R.fragment [ H.div {className: "text-primary center"} [H.text "with DataField"]
             , H.div { className: "nav nav-tabs"} (liItem <$> dbs)
             , H.div {className:"center"} [ H.text $ maybe "" doc db ]
259 260 261 262 263 264 265 266 267
             ]
    where

      db = case datafield of
        (Just (External (Just x))) -> Just x
        _                          -> Nothing

      liItem :: Database -> R.Element
      liItem  df' =
268
        H.div { className : "nav-item nav-link" <> if (Just $ External $ Just df') == datafield then " active" else ""
269
            , on: { click: \_ -> setSearch $ _ { datafield = Just $ External $ Just df' } }
270
            } [ H.text (show df') ]
271
            -}
272

273
databaseInput :: R.State Search
274 275
              -> Array Database
              -> R.Element
276
databaseInput (search /\ setSearch) dbs =
277 278
   H.div { className: "form-group" }
   [ H.div {className: "text-primary center"} [H.text "in database"]
279 280 281
   , R2.select { className: "form-control"
               , on: { change: onChange }
               } (liItem <$> dbs)
282
   , H.div {className:"center"} [ H.text $ maybe "" doc db ]
283
   ]
284
    where
285
      db = case search.datafield of
286 287 288
        (Just (External (Just x))) -> Just x
        _                          -> Nothing

289
      liItem :: Database -> R.Element
290
      liItem  db' = H.option {className : "text-primary center"} [ H.text (show db') ]
291

292
      onChange e = do
293
        let value = read $ R2.unsafeEventValue e
294 295
        setSearch $ _ { datafield = Just $ External value
                      , databases = fromMaybe Empty value
296
                      }
297

298 299 300

orgInput :: R.State Search -> Array Org -> R.Element
orgInput ({datafield} /\ setSearch) orgs =
301 302
  H.div { className: "form-group" }
  [ H.div {className: "text-primary center"} [H.text "filter with organization: "]
303 304 305 306
  , R2.select { className: "form-control"
              , on: { change: onChange }
              } (liItem <$> orgs)
  ]
307 308
    where
      liItem :: Org -> R.Element
309
      liItem  org = H.option {className : "text-primary center"} [ H.text (show org) ]
310
      onChange e = do
311
        let value = R2.unsafeEventValue e
312
        setSearch $ _ { datafield = Just $ External $ Just $ HAL $ read value }
313

314
{-
315 316
filterInput :: R.State String -> R.Element
filterInput (term /\ setTerm) =
317 318 319 320 321 322 323 324 325 326 327 328 329
  H.div { className: "form-group" }
        [ H.input { defaultValue: term
                  , className: "form-control"
                  , type: "text"
                  , on: { change: setTerm <<< const <<< R2.unsafeEventValue }
                  , "required pattern": "[[0-9]+[ ]+]*"
                  -- TODO                          ^FIXME not sure about the regex comprehension: that should match "123 2334 44545" only (Integers separated by one space)
                  -- form validation with CSS
                  -- DOC: https://developer.mozilla.org/en-US/docs/Learn/HTML/Forms/Form_validation
                  , placeholder : "Filter with struct_Ids as integer" 
                  }
         ]
-}
330

331 332 333 334 335 336 337 338 339
type SearchInputProps =
  (
    search :: R.State Search
  )

searchInput :: Record SearchInputProps -> R.Element
searchInput p = R.createElement searchInputComponent p []

searchInputComponent :: R.Component SearchInputProps
340
searchInputComponent = R2.hooksComponent thisModule "searchInput" cpt
341
  where
342 343 344
    cpt {search: (search /\ setSearch)} _ = do
      pure $
        H.div { className : "" }
345 346
        [ H.input { className: "form-control"
                  , defaultValue: search.term
347
                  , on: { change : onChange setSearch }
348
                  , placeholder: "Your Query here"
349
                  , type: "text"
350
                  }
351 352 353 354 355 356
        ]
    onChange setSearch e = do
      let value = R2.unsafeEventValue e
      setSearch $ _ { term = value }

type SubmitButtonProps =
357
  ( onSearch :: GT.AsyncTaskWithType -> Effect Unit
Alexandre Delanoë's avatar
Alexandre Delanoë committed
358 359
  , search   :: R.State Search
  , session  :: Session
360 361 362 363 364 365
  )

submitButton :: Record SubmitButtonProps -> R.Element
submitButton p = R.createElement submitButtonComponent p []

submitButtonComponent :: R.Component SubmitButtonProps
366
submitButtonComponent = R2.hooksComponent thisModule "submitButton" cpt
367
  where
368
    cpt {onSearch, search: (mySearch /\ _), session} _ =
369 370
      pure $
        H.button { className: "btn btn-primary"
371 372 373
                 , "type"   : "button"
                 , on       : {click: doSearch onSearch session mySearch}
                 , style    : { width: "100%" }
374 375
                 } [ H.text "Launch Search" ]

376
    doSearch os s q = \_ -> do
377
      log2 "[submitButton] searching" q
378
      triggerSearch os s q
379 380 381 382
      --case search.term of
      --  "" -> setSearch $ const defaultSearch
      --  _  -> setSearch $ const q

383 384 385 386
triggerSearch :: (GT.AsyncTaskWithType -> Effect Unit)
              -> Session
              -> Search
              -> Effect Unit
387
triggerSearch os s q =
388 389
  launchAff_ $ do
    liftEffect $ do
390 391 392 393 394
      let here = "[triggerSearch] Searching "
      log2 (here <> "databases: ") (show q.databases)
      log2 (here <> "datafield: ") (show q.datafield)
      log2 (here <> "term: ")            q.term
      log2 (here <> "lang: ")      (show q.lang)
395

396 397 398 399 400 401 402
    case q.node_id of
      Nothing -> liftEffect $ log "[triggerSearch] node_id is Nothing, don't know what to do"
      Just id -> do
        task <- performSearch s id $ searchQuery q
        liftEffect $ do
          log2 "[triggerSearch] task" task
          os task
403

404 405 406
    --liftEffect $ do
    --  log2 "Return:" r
    --  modalShow "addCorpus"
407 408 409 410

searchQuery :: Search -> SearchQuery
searchQuery {datafield: Nothing, term} =
  over SearchQuery (_ {query=term}) defaultSearchQuery
411
searchQuery {databases, datafield, lang, term, node_id} =
412 413 414 415 416
  over SearchQuery (_ { databases= databases
                      , datafield= datafield
                      , lang     = lang
                      , query    = term
                      , node_id  = node_id
417
                      }) defaultSearchQuery