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

4 5
import DOM.Simple.Console (log, log2)
import Data.Maybe (Maybe(..), maybe, fromMaybe)
6
import Data.Nullable (null)
7
import Data.Newtype (over)
8
import Data.Set as Set
9
import Data.String (length)
10
import Data.Tuple.Nested ((/\))
11
import Effect (Effect)
12 13
import Effect.Aff (launchAff_)
import Effect.Class (liftEffect)
14
import Gargantext.Components.Forest.Tree.Node.Tools (panel)
15
import Gargantext.Components.Forest.Tree.Node.Action.Search.Types (DataField(..), Database(..), IMT_org(..), Org(..), SearchQuery(..), allIMTorgs, allOrgs, dataFields, defaultSearchQuery, doc, performSearch, datafield2database, Search)
16
import Gargantext.Components.Lang (Lang)
17
import Gargantext.Prelude (Unit, bind, discard, map, pure, show, ($), (<), (<$>), (<>), (==), read)
18
import Gargantext.Sessions (Session)
19
import Gargantext.Components.Forest.Tree.Node.Action.Search.Frame (searchIframes)
20 21
import Gargantext.Types as GT
import Gargantext.Utils.Reactix as R2
22 23
import Reactix as R
import Reactix.DOM.HTML as H
24

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

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

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

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

62 63 64 65 66 67 68 69 70 71 72 73 74 75 76
                             , 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 {} []
77
 
78
                             , H.div {} [ searchIframes { iframeRef, search } ]
79

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

85
                ]
86
      let button =  submitButton {onSearch, search, session: props.session}
87

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


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
107
      ]
108

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

115

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

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

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

138

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

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

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


isIn :: IMT_org -> Maybe DataField -> Boolean
178 179 180 181 182 183 184 185 186 187 188
isIn org ( Just
           ( External
             ( Just
               ( HAL
                 ( Just
                   ( IMT imtOrgs )
                 )
               )
             )
           )
         ) = Set.member org imtOrgs
189 190 191 192 193
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')))))
194 195
  where
    imtOrgs' = if Set.member org imtOrgs
196 197 198
                  then
                    if org == All_IMT
                       then Set.empty
199
                       else Set.delete All_IMT $ Set.delete org imtOrgs
200 201 202 203
                  else
                    if org == All_IMT
                       then Set.fromFoldable allIMTorgs
                       else Set.insert org imtOrgs
204

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

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

------------------------------------------------------------------------
225 226
dataFieldNav :: R.State Search -> Array DataField -> R.Element
dataFieldNav ({datafield} /\ setSearch) datafields =
Alexandre Delanoë's avatar
Alexandre Delanoë committed
227 228 229 230 231
  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 
                                                   ]
232 233 234
             ]
    where
      liItem :: DataField -> R.Element
235
      liItem  df' =
236 237 238 239 240
        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
241
                                               , databases = datafield2database df'
242
                                               }
Alexandre Delanoë's avatar
Alexandre Delanoë committed
243
                  }
244 245
            -- just one database query for now
            -- a list a selected database needs more ergonomy
246
            } [ H.text (show df') ]
247 248

------------------------------------------------------------------------
249
{-
250 251 252 253
databaseNav  :: R.State Search
              -> Array Database
              -> R.Element
databaseNav ({datafield} /\ setSearch) dbs =
254 255 256
  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 ]
257 258 259 260 261 262 263 264 265
             ]
    where

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

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

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

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

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

296 297 298

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

312
{-
313 314
filterInput :: R.State String -> R.Element
filterInput (term /\ setTerm) =
315 316 317 318 319 320 321 322 323 324 325 326 327
  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" 
                  }
         ]
-}
328

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

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

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

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

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

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

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

382 383 384 385
triggerSearch :: (GT.AsyncTaskWithType -> Effect Unit)
              -> Session
              -> Search
              -> Effect Unit
386
triggerSearch os s q =
387 388
  launchAff_ $ do
    liftEffect $ do
389 390 391 392 393
      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)
394

395 396 397 398 399 400 401
    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
402

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

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