From 99fe38fff8b26de792d4f31aeef97a656ba6b007 Mon Sep 17 00:00:00 2001
From: Przemek Kaminski <pk@intrepidus.pl>
Date: Mon, 19 Oct 2020 07:59:59 +0200
Subject: [PATCH] [table] cache off search works now correctly

---
 src/Gargantext/Components/DocsTable.purs      | 154 ++++++----
 .../Nodes/Annuaire/User/Contacts/Tabs.purs    |  14 +-
 .../Components/Nodes/Corpus/Document.purs     | 270 +----------------
 .../Nodes/Corpus/Document/Types.purs          | 280 ++++++++++++++++++
 .../Components/Nodes/Corpus/Types.purs        |   1 +
 src/Gargantext/Components/Nodes/Texts.purs    | 180 ++++++-----
 src/Gargantext/Components/Tab.purs            |   6 +-
 7 files changed, 494 insertions(+), 411 deletions(-)
 create mode 100644 src/Gargantext/Components/Nodes/Corpus/Document/Types.purs

diff --git a/src/Gargantext/Components/DocsTable.purs b/src/Gargantext/Components/DocsTable.purs
index f2917465..25982aab 100644
--- a/src/Gargantext/Components/DocsTable.purs
+++ b/src/Gargantext/Components/DocsTable.purs
@@ -29,46 +29,61 @@ import Reactix.DOM.HTML as H
 ------------------------------------------------------------------------
 import Gargantext.Prelude
 import Gargantext.Components.Category
+import Gargantext.Components.Nodes.Lists.Types as NT
 import Gargantext.Components.Table as T
 import Gargantext.Ends (Frontends, url)
-import Gargantext.Hooks.Loader (useLoaderWithCacheAPI, HashedResponse(..))
+import Gargantext.Hooks.Loader (useLoader, useLoaderWithCacheAPI, HashedResponse(..))
 import Gargantext.Utils.Seq (sortWith) as Seq
 import Gargantext.Utils.Reactix as R2
 import Gargantext.Routes as Routes
 import Gargantext.Routes (SessionRoute(NodeAPI))
 import Gargantext.Sessions (Session, sessionId, get, delete, put)
-import Gargantext.Types (NodeType(..), OrderBy(..), TableResult, TabType, showTabType')
+import Gargantext.Types (NodeType(..), OrderBy(..), TableResult, TabSubType(..), TabType, showTabType')
 import Gargantext.Utils.CacheAPI as GUC
 import Gargantext.Utils.Reactix as R2
 
+thisModule :: String
 thisModule = "Gargantext.Components.DocsTable"
 ------------------------------------------------------------------------
 
 type TotalRecords = Int
 
-type LayoutProps =
-  ( nodeId       :: Int
-  , totalRecords :: Int
+type Path a = (
+    corpusId :: Int
+  , listId :: Int
+  , frontends :: Frontends
+  , session :: Session
+  , tabType :: TabSubType a
+  )
+
+type LayoutProps = (
+    cacheState   :: R.State NT.CacheState
+  , corpusId     :: Maybe Int
+  , frontends    :: Frontends
   , chart        :: R.Element
-  , tabType      :: TabType
   , listId       :: Int
-  , corpusId     :: Maybe Int
+  , nodeId       :: Int
+  -- , path         :: Record (Path a)
+  , session      :: Session
   , showSearch   :: Boolean
-  , frontends    :: Frontends
-  , session      :: Session )
+  , tabType      :: TabType
   -- ^ tabType is not ideal here since it is too much entangled with tabs and
   -- ngramtable. Let's see how this evolves.  )
-
-type PageLayoutProps =
-  ( nodeId       :: Int
   , totalRecords :: Int
-  , tabType      :: TabType
-  , listId       :: Int
+  )
+
+type PageLayoutProps = (
+    cacheState   :: R.State NT.CacheState
   , corpusId     :: Maybe Int
+  , frontends    :: Frontends
+  , listId       :: Int
+  , nodeId       :: Int
+  , params       :: T.Params
   , query        :: Query
   , session      :: Session
-  , frontends    :: Frontends
-  , params       :: T.Params )
+  , tabType      :: TabType
+  , totalRecords :: Int
+  )
 
 type LocalCategories = Map Int Category
 type Query = String
@@ -150,14 +165,13 @@ instance decodeHyperdata :: DecodeJson Hyperdata where
 instance decodeResponse :: DecodeJson Response where
   decodeJson json = do
     obj        <- decodeJson json
-    cid        <- obj .: "id"
     category   <- obj .: "category"
-    ngramCount <- obj .: "id"
-    title  <- obj .: "title"
+    cid        <- obj .: "id"
     hyperdata  <- obj .: "hyperdata"
+    ngramCount <- obj .: "id"
+    title      <- obj .: "title"
     pure $ Response { cid, title, category: decodeCategory category, ngramCount, hyperdata }
 
-
 docViewLayout :: Record LayoutProps -> R.Element
 docViewLayout props = R.createElement docViewLayoutCpt props []
 
@@ -167,7 +181,7 @@ docViewLayoutCpt = R.hooksComponentWithModule thisModule "docViewLayout" cpt
     cpt layout _children = do
       query <- R.useState' ""
       let params = T.initialParams
-      pure $ docView {query, params, layout}
+      pure $ docView { layout, params, query }
 
 type Props = (
     layout :: Record LayoutProps
@@ -180,15 +194,27 @@ docView props = R.createElement docViewCpt props []
 
 docViewCpt :: R.Component Props
 docViewCpt = R.hooksComponentWithModule thisModule "docView" cpt where
-  cpt { query, params
-      , layout: { frontends, session, nodeId, tabType, listId
-                , corpusId, totalRecords, chart, showSearch } } _ = do
+  cpt { layout: { cacheState
+                , chart
+                , corpusId
+                , frontends
+                , listId
+                , nodeId
+                , session
+                , showSearch
+                , tabType
+                , totalRecords
+                }
+      , params
+      , query
+      } _ = do
     pure $ H.div {className: "container1"}
       [ R2.row
         [ chart
         , if showSearch then searchBar query else H.div {} []
         , H.div {className: "col-md-12"}
-          [ pageLayout { corpusId
+          [ pageLayout { cacheState
+                       , corpusId
                        , frontends
                        , listId
                        , nodeId
@@ -198,20 +224,6 @@ docViewCpt = R.hooksComponentWithModule thisModule "docView" cpt where
                        , tabType
                        , totalRecords
                        } ] ] ]
-    -- onClickTrashAll nodeId _ = do
-    --   launchAff $ deleteAllDocuments p.session nodeId
-          
-          {-, H.div {className: "col-md-1 col-md-offset-11"}
-            [ pageLayout p.session params {nodeId, totalRecords, tabType, listId, corpusId, query: fst query} ]
-          , H.div {className: "col-md-1 col-md-offset-11"}
-            [ H.button { className: "btn"
-                       , style: {backgroundColor: "peru", color : "white", border : "white"}
-                       , on: { click: onClickTrashAll nodeId } }
-              [  H.i {className: "glyphitem glyphicon glyphicon-trash"} []
-              ,  H.text "Trash all"
-              ]
-            ]
-           -}
 
 searchBar :: R.State Query -> R.Element
 searchBar (query /\ setQuery) = R.createElement el {} []
@@ -302,30 +314,40 @@ pageLayout props = R.createElement pageLayoutCpt props []
 
 pageLayoutCpt :: R.Component PageLayoutProps
 pageLayoutCpt = R.hooksComponentWithModule thisModule "pageLayout" cpt where
-  cpt props@{ corpusId, frontends, listId, nodeId, params, query, session, tabType } _ =
-    useLoaderWithCacheAPI {
-        cacheEndpoint: getPageHash session
-      , handleResponse
-      , mkRequest
-      , path
-      , renderer: paint
-      }
-    where
-      path = { corpusId, listId, nodeId, params, query, tabType }
-      paint (Tuple count docs) = page params (props { totalRecords = count }) docs
-
-      mkRequest :: PageParams -> GUC.Request
-      mkRequest p@{ listId, nodeId, tabType } =
-        GUC.makeGetRequest session $ tableRoute nodeId tabType listId
-      handleResponse :: HashedResponse (TableResult Response) -> Tuple Int (Array DocumentsView)
-      handleResponse (HashedResponse { hash, value: res }) = ret
-        where
-          docs = res2corpus <$> filterDocs query res.docs
-          ret = if mock then
-                    --Tuple 0 (take limit $ drop offset sampleData)
-                    Tuple 0 sampleData
-                  else
-                    Tuple (A.length docs) docs
+  cpt props@{ cacheState, corpusId, frontends, listId, nodeId, params, query, session, tabType } _ = do
+    let path = { corpusId, listId, nodeId, params, query, tabType }
+        handleResponse :: HashedResponse (TableResult Response) -> Tuple Int (Array DocumentsView)
+        handleResponse (HashedResponse { hash, value: res }) = ret
+          where
+            docs = res2corpus <$> filterDocs query res.docs
+            ret = if mock then
+                      --Tuple 0 (take limit $ drop offset sampleData)
+                      Tuple 0 sampleData
+                    else
+                      Tuple (A.length docs) docs
+    case cacheState of
+      (NT.CacheOn  /\ _) -> do
+        let paint (Tuple count docs) = page params (props { totalRecords = count }) docs
+            mkRequest :: PageParams -> GUC.Request
+            mkRequest p@{ listId, nodeId, tabType } =
+              GUC.makeGetRequest session $ tableRoute nodeId tabType listId
+
+        useLoaderWithCacheAPI {
+            cacheEndpoint: getPageHash session
+          , handleResponse
+          , mkRequest
+          , path
+          , renderer: paint
+          }
+      (NT.CacheOff /\ _) -> do
+        paramsS <- R.useState' params
+        let loader p@{ listId, nodeId, tabType } = do
+              res <- get session $ tableRouteWithPage { listId, nodeId, params: fst paramsS, tabType }
+              pure $ handleResponse res
+            render (Tuple count documents) = pagePaint { documents
+                                                       , layout: props { totalRecords = count }
+                                                       , params: paramsS }
+        useLoader (path { params = fst paramsS }) loader render
 
 type PageProps = (
     documents :: Array DocumentsView
@@ -451,6 +473,12 @@ tableRoute nodeId tabType listId = NodeAPI Node (Just nodeId) $ "table" <> "?tab
 tableHashRoute :: Int -> TabType -> SessionRoute
 tableHashRoute nodeId tabType = NodeAPI Node (Just nodeId) $ "table/hash" <> "?tabType=" <> (showTabType' tabType)
 
+tableRouteWithPage :: { listId :: Int
+                     , nodeId :: Int
+                     , params :: T.Params
+                     , tabType :: TabType } -> SessionRoute
+tableRouteWithPage { listId, nodeId, params: { limit, offset, orderBy, searchType }, tabType } = NodeAPI Node (Just nodeId) $ "table" <> "?tabType=" <> (showTabType' tabType) <> "&list=" <> (show listId) <> "&limit=" <> (show limit) <> "&offset=" <> (show offset) <> "&orderBy=" <> (show orderBy) <> "&searchType=" <> (show searchType)
+
 deleteAllDocuments :: Session -> Int -> Aff (Array Int)
 deleteAllDocuments session = delete session <<< documentsRoute
 
diff --git a/src/Gargantext/Components/Nodes/Annuaire/User/Contacts/Tabs.purs b/src/Gargantext/Components/Nodes/Annuaire/User/Contacts/Tabs.purs
index e5a8f157..b15c20e1 100644
--- a/src/Gargantext/Components/Nodes/Annuaire/User/Contacts/Tabs.purs
+++ b/src/Gargantext/Components/Nodes/Annuaire/User/Contacts/Tabs.purs
@@ -76,11 +76,17 @@ tabsCpt = R.hooksComponentWithModule thisModule "tabs" cpt
             chart       = mempty
             totalRecords = 4736 -- TODO
             docs = DT.docViewLayout
-              { frontends, session, nodeId, chart, totalRecords
-              , tabType: TabPairing TabDocs
-              , listId: defaultListId
+              { cacheState
+              , chart
               , corpusId: Nothing
-              , showSearch: true }
+              , frontends
+              , listId: defaultListId
+              , nodeId
+              , session
+              , showSearch: true
+              , tabType: TabPairing TabDocs
+              , totalRecords
+              }
 
 
 type NgramsViewTabsProps =
diff --git a/src/Gargantext/Components/Nodes/Corpus/Document.purs b/src/Gargantext/Components/Nodes/Corpus/Document.purs
index 831b3670..dfdb0229 100644
--- a/src/Gargantext/Components/Nodes/Corpus/Document.purs
+++ b/src/Gargantext/Components/Nodes/Corpus/Document.purs
@@ -12,9 +12,10 @@ import Reactix.DOM.HTML as H
 
 import Gargantext.Prelude
 
-import Gargantext.Components.AutoUpdate ( autoUpdate)
+import Gargantext.Components.AutoUpdate (autoUpdate)
 import Gargantext.Components.Search (SearchType(..))
 import Gargantext.Components.Node (NodePoly(..))
+import Gargantext.Components.Nodes.Corpus.Document.Types
 import Gargantext.Components.NgramsTable.Core
   ( CoreState, NgramsPatch(..), NgramsTerm, Replace, Versioned(..)
   , VersionedNgramsTable, addNewNgram, applyNgramsTablePatch, commitPatch
@@ -23,282 +24,19 @@ import Gargantext.Components.Annotation.AnnotatedField as AnnotatedField
 import Gargantext.Hooks.Loader (useLoader)
 import Gargantext.Routes (SessionRoute(..))
 import Gargantext.Sessions (Session, get, sessionId)
-import Gargantext.Types (CTabNgramType(..), NodeType(..), TabSubType(..), TabType(..), TermList, ScoreType(..))
+import Gargantext.Types (CTabNgramType(..), NodeType(..), TabSubType(..), TabType(..), ScoreType(..))
 import Gargantext.Utils as U
 import Gargantext.Utils.Reactix as R2
 
+thisModule :: String
 thisModule = "Gargantext.Components.Nodes.Corpus.Document"
 
-type DocPath =
-  {
-    corpusId :: Maybe Int
-  , listIds  :: Array Int
-  , nodeId   :: Int
-  , session  :: Session
-  , tabType  :: TabType
-  }
-
-type NodeDocument = NodePoly Document
-
-type LoadedData =
-  { document    :: NodeDocument
-  , ngramsTable :: VersionedNgramsTable
-  }
-
-type Props = (
-    loaded :: LoadedData
-  , path   :: DocPath
-  )
-
--- This is a subpart of NgramsTable.State.
-type State = CoreState ()
-
-initialState
-  :: forall props others
-  .  { loaded :: { ngramsTable :: VersionedNgramsTable | others }
-     | props }
-  -> State
-initialState {loaded: {ngramsTable: Versioned {version}}} =
-  { ngramsLocalPatch: mempty
-  , ngramsStagePatch: mempty
-  , ngramsValidPatch: mempty
-  , ngramsVersion:    version
-  }
-
--- This is a subset of NgramsTable.Action.
-data Action
-  = SetTermListItem NgramsTerm (Replace TermList)
-  | AddNewNgram NgramsTerm TermList
-  | Synchronize
-
-newtype Status = Status { failed    :: Int
-                        , succeeded :: Int
-                        , remaining :: Int
-                        }
-
-newtype DocumentV3 =
-  DocumentV3 { abstract           :: Maybe String
-             , authors            :: Maybe String
-             --, error              :: Maybe String
-             , language_iso2      :: Maybe String
-             , language_iso3      :: Maybe String
-             , language_name      :: Maybe String
-             , publication_date   :: Maybe String
-             , publication_day    :: Maybe Int
-             , publication_hour   :: Maybe Int
-             , publication_minute :: Maybe Int
-             , publication_month  :: Maybe Int
-             , publication_second :: Maybe Int
-             , publication_year   :: Maybe Int
-             , realdate_full_     :: Maybe String
-             , source             :: Maybe String
-             , statuses           :: Maybe (Array Status)
-             , title              :: Maybe String
-             }
-
-defaultNodeDocumentV3 :: NodePoly DocumentV3
-defaultNodeDocumentV3 =
-  NodePoly { id : 0
-           , typename : 0
-           , userId   : 0
-           , parentId : 0
-           , name     : "Default name"
-           , date     : "Default date"
-           , hyperdata : defaultDocumentV3
-         }
-
-defaultDocumentV3 :: DocumentV3
-defaultDocumentV3 =
-  DocumentV3 { abstract           : Nothing
-             , authors            : Nothing
-             --, error              : Nothing
-             , language_iso2      : Nothing
-             , language_iso3      : Nothing
-             , language_name      : Nothing
-             , publication_date   : Nothing
-             , publication_day    : Nothing
-             , publication_hour   : Nothing
-             , publication_minute : Nothing
-             , publication_month  : Nothing
-             , publication_second : Nothing
-             , publication_year   : Nothing
-             , realdate_full_     : Nothing
-             , source             : Nothing
-             , statuses           : Nothing
-             , title              : Nothing
-             }
-
-data Document
-  = Document
-    { abstract           :: Maybe String
-    , authors            :: Maybe String
-    , bdd                :: Maybe String
-    , doi                :: Maybe String
-    , language_iso2      :: Maybe String
-    -- , page               :: Maybe Int
-    , publication_date   :: Maybe String
-    --, publication_second :: Maybe Int
-    --, publication_minute :: Maybe Int
-    --, publication_hour   :: Maybe Int
-    , publication_day    :: Maybe Int
-    , publication_month  :: Maybe Int
-    , publication_year   :: Maybe Int
-    , source             :: Maybe String
-    , institutes         :: Maybe String
-    , title              :: Maybe String
-    , uniqId             :: Maybe String
-    --, url                :: Maybe String
-    --, text               :: Maybe String
-    }
-
 publicationDate :: Document -> String
 publicationDate (Document doc@{publication_year: Nothing}) = ""
 publicationDate (Document doc@{publication_year: Just py, publication_month: Nothing}) = U.zeroPad 2 py
 publicationDate (Document doc@{publication_year: Just py, publication_month: Just pm, publication_day: Nothing}) = (U.zeroPad 2 py) <> "-" <> (U.zeroPad 2 pm)
 publicationDate (Document doc@{publication_year: Just py, publication_month: Just pm, publication_day: Just pd}) = (U.zeroPad 2 py) <> "-" <> (U.zeroPad 2 pm) <> "-" <> (U.zeroPad 2 pd)
 
-defaultNodeDocument :: NodeDocument
-defaultNodeDocument =
-  NodePoly { id : 0
-           , typename : 0
-           , userId   : 0
-           , parentId : 0
-           , name     : "Default name"
-           , date     : "Default date"
-           , hyperdata : defaultDocument
-         }
-
--- TODO: BUG if DOI does not exist, page is not shown
-defaultDocument :: Document
-defaultDocument =
-  Document { abstract           : Nothing
-           , authors            : Nothing
-           , bdd                : Nothing
-           , doi                : Nothing
-           , language_iso2      : Nothing
-           --, page               : Nothing
-           , publication_date   : Nothing
-           --, publication_second : Nothing
-           --, publication_minute : Nothing
-           --, publication_hour   : Nothing
-           , publication_day    : Nothing
-           , publication_month  : Nothing
-           , publication_year   : Nothing
-           , source             : Nothing
-           , institutes         : Nothing
-           , title              : Nothing
-           , uniqId             : Nothing
-           --, url                : Nothing
-           --, text               : Nothing
-           }
-
-derive instance genericDocument   :: Generic Document   _
-derive instance genericDocumentV3 :: Generic DocumentV3 _
-derive instance genericStatus     :: Generic Status     _
-
-instance showDocument :: Show Document where
-  show = genericShow
-
-instance showDocumentV3 :: Show DocumentV3 where
-  show = genericShow
-
-instance showStatus :: Show Status where
-  show = genericShow
-
-instance decodeStatus :: DecodeJson Status
-  where
-    decodeJson json = do
-      obj <- decodeJson json
-      failed <- obj .: "failed"
-      succeeded <- obj .: "succeeded"
-      remaining <- obj .: "remaining"
-      pure $ Status {failed, succeeded, remaining}
-
-
-instance decodeDocumentV3 :: DecodeJson DocumentV3
-  where
-    decodeJson json = do
-      obj <- decodeJson json
-      abstract <- obj .:? "abstract"
-      authors  <- obj .: "authors"
-      --error    <- obj .: "error"
-      language_iso2 <- obj .: "language_iso2"
-      language_iso3 <- obj .: "language_iso3"
-      language_name <- obj .: "language_name"
-      publication_date   <- obj .: "publication_date"
-      publication_day    <- obj .: "publication_day"
-      publication_hour   <- obj .: "publication_hour"
-      publication_minute <- obj .: "publication_minute"
-      publication_month  <- obj .: "publication_month"
-      publication_second <- obj .: "publication_second"
-      publication_year   <- obj .: "publication_year"
-      realdate_full_     <- obj .: "realdate_full_"
-      source   <- obj .: "source"
-      statuses <- obj .: "statuses"
-      title    <- obj .: "title"
-      pure $ DocumentV3 { abstract
-                        , authors
-                        --, error
-                        , language_iso2
-                        , language_iso3
-                        , language_name
-                        , publication_date
-                        , publication_day
-                        , publication_hour
-                        , publication_minute
-                        , publication_month
-                        , publication_second
-                        , publication_year
-                        , realdate_full_
-                        , source
-                        , statuses
-                        , title
-                        }
-
-instance decodeDocument :: DecodeJson Document
-  where
-    decodeJson json = do
-      obj <- decodeJson json
-      abstract <- obj .:? "abstract"
-      authors  <- obj .:? "authors"
-      bdd      <- obj .:? "bdd"
-      doi      <- obj .:? "doi"
-      language_iso2 <- obj .:? "language_iso2"
-      -- page          <- obj .:? "page"
-      publication_date   <- obj .:? "publication_date"
-      --publication_second <- obj .:? "publication_second"
-      --publication_minute <- obj .:? "publication_minute"
-      --publication_hour   <- obj .:? "publication_hour"
-      publication_day    <- obj .:? "publication_day"
-      publication_month  <- obj .:? "publication_month"
-      publication_year   <- obj .:? "publication_year"
-      source             <- obj .:? "sources"
-      institutes         <- obj .:? "institutes"
-      title              <- obj .:? "title"
-      uniqId             <- obj .:? "uniqId"
-      --url                <- obj .: "url"
-      --text               <- obj .: "text"
-      pure $ Document { abstract
-                      , authors
-                      , bdd
-                      , doi
-                      , language_iso2
-                      -- , page
-                      , publication_date
-                      --, publication_second
-                      --, publication_minute
-                      --, publication_hour
-                      , publication_day
-                      , publication_month
-                      , publication_year
-                      , source
-                      , institutes
-                      , title
-                      , uniqId
-                      --, url
-                      --, text
-                      }
-
 docViewWrapper :: Record Props -> R.Element
 docViewWrapper props = R.createElement docViewWrapperCpt props []
 
diff --git a/src/Gargantext/Components/Nodes/Corpus/Document/Types.purs b/src/Gargantext/Components/Nodes/Corpus/Document/Types.purs
new file mode 100644
index 00000000..f76d50e5
--- /dev/null
+++ b/src/Gargantext/Components/Nodes/Corpus/Document/Types.purs
@@ -0,0 +1,280 @@
+module Gargantext.Components.Nodes.Corpus.Document.Types where
+
+import Data.Argonaut (class DecodeJson, decodeJson, (.:), (.:?))
+import Data.Generic.Rep (class Generic)
+import Data.Generic.Rep.Show (genericShow)
+import Data.Maybe (Maybe(..))
+
+import Gargantext.Prelude
+
+import Gargantext.Components.Node (NodePoly(..))
+import Gargantext.Components.NgramsTable.Core
+  (CoreState, NgramsTerm, Replace, Versioned(..) , VersionedNgramsTable)
+import Gargantext.Components.Annotation.AnnotatedField as AnnotatedField
+import Gargantext.Sessions (Session)
+import Gargantext.Types (TabType, TermList)
+
+type DocPath =
+  {
+    corpusId :: Maybe Int
+  , listIds  :: Array Int
+  , nodeId   :: Int
+  , session  :: Session
+  , tabType  :: TabType
+  }
+
+type NodeDocument = NodePoly Document
+
+type LoadedData =
+  { document    :: NodeDocument
+  , ngramsTable :: VersionedNgramsTable
+  }
+
+type Props = (
+    loaded :: LoadedData
+  , path   :: DocPath
+  )
+
+-- This is a subpart of NgramsTable.State.
+type State = CoreState ()
+
+initialState
+  :: forall props others
+  .  { loaded :: { ngramsTable :: VersionedNgramsTable | others }
+     | props }
+  -> State
+initialState {loaded: {ngramsTable: Versioned {version}}} =
+  { ngramsLocalPatch: mempty
+  , ngramsStagePatch: mempty
+  , ngramsValidPatch: mempty
+  , ngramsVersion:    version
+  }
+
+-- This is a subset of NgramsTable.Action.
+data Action
+  = SetTermListItem NgramsTerm (Replace TermList)
+  | AddNewNgram NgramsTerm TermList
+  | Synchronize
+
+newtype Status = Status { failed    :: Int
+                        , succeeded :: Int
+                        , remaining :: Int
+                        }
+
+newtype DocumentV3 =
+  DocumentV3 { abstract           :: Maybe String
+             , authors            :: Maybe String
+             --, error              :: Maybe String
+             , language_iso2      :: Maybe String
+             , language_iso3      :: Maybe String
+             , language_name      :: Maybe String
+             , publication_date   :: Maybe String
+             , publication_day    :: Maybe Int
+             , publication_hour   :: Maybe Int
+             , publication_minute :: Maybe Int
+             , publication_month  :: Maybe Int
+             , publication_second :: Maybe Int
+             , publication_year   :: Maybe Int
+             , realdate_full_     :: Maybe String
+             , source             :: Maybe String
+             , statuses           :: Maybe (Array Status)
+             , title              :: Maybe String
+             }
+
+defaultNodeDocumentV3 :: NodePoly DocumentV3
+defaultNodeDocumentV3 =
+  NodePoly { id : 0
+           , typename : 0
+           , userId   : 0
+           , parentId : 0
+           , name     : "Default name"
+           , date     : "Default date"
+           , hyperdata : defaultDocumentV3
+         }
+
+defaultDocumentV3 :: DocumentV3
+defaultDocumentV3 =
+  DocumentV3 { abstract           : Nothing
+             , authors            : Nothing
+             --, error              : Nothing
+             , language_iso2      : Nothing
+             , language_iso3      : Nothing
+             , language_name      : Nothing
+             , publication_date   : Nothing
+             , publication_day    : Nothing
+             , publication_hour   : Nothing
+             , publication_minute : Nothing
+             , publication_month  : Nothing
+             , publication_second : Nothing
+             , publication_year   : Nothing
+             , realdate_full_     : Nothing
+             , source             : Nothing
+             , statuses           : Nothing
+             , title              : Nothing
+             }
+
+data Document
+  = Document
+    { abstract           :: Maybe String
+    , authors            :: Maybe String
+    , bdd                :: Maybe String
+    , doi                :: Maybe String
+    , language_iso2      :: Maybe String
+    -- , page               :: Maybe Int
+    , publication_date   :: Maybe String
+    --, publication_second :: Maybe Int
+    --, publication_minute :: Maybe Int
+    --, publication_hour   :: Maybe Int
+    , publication_day    :: Maybe Int
+    , publication_month  :: Maybe Int
+    , publication_year   :: Maybe Int
+    , source             :: Maybe String
+    , institutes         :: Maybe String
+    , title              :: Maybe String
+    , uniqId             :: Maybe String
+    --, url                :: Maybe String
+    --, text               :: Maybe String
+    }
+
+
+defaultNodeDocument :: NodeDocument
+defaultNodeDocument =
+  NodePoly { id : 0
+           , typename : 0
+           , userId   : 0
+           , parentId : 0
+           , name     : "Default name"
+           , date     : "Default date"
+           , hyperdata : defaultDocument
+         }
+
+-- TODO: BUG if DOI does not exist, page is not shown
+defaultDocument :: Document
+defaultDocument =
+  Document { abstract           : Nothing
+           , authors            : Nothing
+           , bdd                : Nothing
+           , doi                : Nothing
+           , language_iso2      : Nothing
+           --, page               : Nothing
+           , publication_date   : Nothing
+           --, publication_second : Nothing
+           --, publication_minute : Nothing
+           --, publication_hour   : Nothing
+           , publication_day    : Nothing
+           , publication_month  : Nothing
+           , publication_year   : Nothing
+           , source             : Nothing
+           , institutes         : Nothing
+           , title              : Nothing
+           , uniqId             : Nothing
+           --, url                : Nothing
+           --, text               : Nothing
+           }
+
+derive instance genericDocument   :: Generic Document   _
+derive instance genericDocumentV3 :: Generic DocumentV3 _
+derive instance genericStatus     :: Generic Status     _
+
+instance showDocument :: Show Document where
+  show = genericShow
+
+instance showDocumentV3 :: Show DocumentV3 where
+  show = genericShow
+
+instance showStatus :: Show Status where
+  show = genericShow
+
+instance decodeStatus :: DecodeJson Status
+  where
+    decodeJson json = do
+      obj <- decodeJson json
+      failed <- obj .: "failed"
+      succeeded <- obj .: "succeeded"
+      remaining <- obj .: "remaining"
+      pure $ Status {failed, succeeded, remaining}
+
+
+instance decodeDocumentV3 :: DecodeJson DocumentV3
+  where
+    decodeJson json = do
+      obj <- decodeJson json
+      abstract <- obj .:? "abstract"
+      authors  <- obj .: "authors"
+      --error    <- obj .: "error"
+      language_iso2 <- obj .: "language_iso2"
+      language_iso3 <- obj .: "language_iso3"
+      language_name <- obj .: "language_name"
+      publication_date   <- obj .: "publication_date"
+      publication_day    <- obj .: "publication_day"
+      publication_hour   <- obj .: "publication_hour"
+      publication_minute <- obj .: "publication_minute"
+      publication_month  <- obj .: "publication_month"
+      publication_second <- obj .: "publication_second"
+      publication_year   <- obj .: "publication_year"
+      realdate_full_     <- obj .: "realdate_full_"
+      source   <- obj .: "source"
+      statuses <- obj .: "statuses"
+      title    <- obj .: "title"
+      pure $ DocumentV3 { abstract
+                        , authors
+                        --, error
+                        , language_iso2
+                        , language_iso3
+                        , language_name
+                        , publication_date
+                        , publication_day
+                        , publication_hour
+                        , publication_minute
+                        , publication_month
+                        , publication_second
+                        , publication_year
+                        , realdate_full_
+                        , source
+                        , statuses
+                        , title
+                        }
+
+instance decodeDocument :: DecodeJson Document
+  where
+    decodeJson json = do
+      obj <- decodeJson json
+      abstract <- obj .:? "abstract"
+      authors  <- obj .:? "authors"
+      bdd      <- obj .:? "bdd"
+      doi      <- obj .:? "doi"
+      language_iso2 <- obj .:? "language_iso2"
+      -- page          <- obj .:? "page"
+      publication_date   <- obj .:? "publication_date"
+      --publication_second <- obj .:? "publication_second"
+      --publication_minute <- obj .:? "publication_minute"
+      --publication_hour   <- obj .:? "publication_hour"
+      publication_day    <- obj .:? "publication_day"
+      publication_month  <- obj .:? "publication_month"
+      publication_year   <- obj .:? "publication_year"
+      source             <- obj .:? "sources"
+      institutes         <- obj .:? "institutes"
+      title              <- obj .:? "title"
+      uniqId             <- obj .:? "uniqId"
+      --url                <- obj .: "url"
+      --text               <- obj .: "text"
+      pure $ Document { abstract
+                      , authors
+                      , bdd
+                      , doi
+                      , language_iso2
+                      -- , page
+                      , publication_date
+                      --, publication_second
+                      --, publication_minute
+                      --, publication_hour
+                      , publication_day
+                      , publication_month
+                      , publication_year
+                      , source
+                      , institutes
+                      , title
+                      , uniqId
+                      --, url
+                      --, text
+                      }
diff --git a/src/Gargantext/Components/Nodes/Corpus/Types.purs b/src/Gargantext/Components/Nodes/Corpus/Types.purs
index def772ab..38efb6e5 100644
--- a/src/Gargantext/Components/Nodes/Corpus/Types.purs
+++ b/src/Gargantext/Components/Nodes/Corpus/Types.purs
@@ -8,6 +8,7 @@ import Data.Generic.Rep (class Generic)
 import Data.Generic.Rep.Eq (genericEq)
 import Data.Generic.Rep.Show (genericShow)
 import Data.Maybe (Maybe(..))
+
 import Gargantext.Components.Node (NodePoly)
 import Gargantext.Prelude
 
diff --git a/src/Gargantext/Components/Nodes/Texts.purs b/src/Gargantext/Components/Nodes/Texts.purs
index ceaf4a7c..82f57348 100644
--- a/src/Gargantext/Components/Nodes/Texts.purs
+++ b/src/Gargantext/Components/Nodes/Texts.purs
@@ -60,9 +60,8 @@ textsLayoutWithKeyCpt = R.hooksComponentWithModule thisModule "textsLayoutWithKe
 
       pure $ loader {session, nodeId} loadCorpusWithChild $
         \corpusData@{ corpusId, corpusNode, defaultListId } -> do
-          let NodePoly { name, date, hyperdata: Hyperdata h } = corpusNode
+          let NodePoly { date, hyperdata: Hyperdata h, name } = corpusNode
               CorpusInfo { authors, desc, query } = getCorpusInfo h.fields
-              tabs' = tabs { corpusData, corpusId, frontends, session }
               title = "Corpus " <> name
 
           R.fragment [
@@ -73,7 +72,7 @@ textsLayoutWithKeyCpt = R.hooksComponentWithModule thisModule "textsLayoutWithKe
                                       , query
                                       , title
                                       , user: authors }
-            , tabs'
+            , tabs { cacheState, corpusData, corpusId, frontends, session }
           ]
 
 data Mode = MoreLikeFav | MoreLikeTrash
@@ -89,7 +88,13 @@ modeTabType :: Mode -> CTabNgramType
 modeTabType MoreLikeFav    = CTabAuthors  -- TODO
 modeTabType MoreLikeTrash  = CTabSources  -- TODO
 
-type TabsProps = ( frontends :: Frontends, session :: Session, corpusId :: Int, corpusData :: CorpusData )
+type TabsProps = (
+    cacheState :: R.State NT.CacheState
+  , corpusData :: CorpusData
+  , corpusId :: Int
+  , frontends :: Frontends
+  , session :: Session
+  )
 
 tabs :: Record TabsProps -> R.Element
 tabs props = R.createElement tabsCpt props []
@@ -97,27 +102,43 @@ tabs props = R.createElement tabsCpt props []
 tabsCpt :: R.Component TabsProps
 tabsCpt = R.hooksComponentWithModule thisModule "tabs" cpt
   where
-    cpt {frontends, session, corpusId, corpusData} _ = do
+    cpt { cacheState, corpusId, corpusData, frontends, session } _ = do
       (selected /\ setSelected) <- R.useState' 0
-      pure $ Tab.tabs { tabs: tabs', selected }
+
+      let path = initialPath
+
+      pure $ Tab.tabs {
+          selected
+        , tabs: [
+            "Documents"       /\ R.fragment [
+                histo { path, session }
+              , docView' path TabDocs
+              ]
+          , "Trash"           /\ docView' path TabTrash
+          , "More like fav"   /\ docView' path TabMoreLikeFav
+          , "More like trash" /\ docView' path TabMoreLikeTrash
+          ]
+        }
+
       where
-        tabs' = [ "Documents"     /\ docs,        "Trash"           /\ trash
-                , "More like fav" /\ moreLikeFav, "More like trash" /\ moreLikeTrash ]
-        docView' tabType = docView { frontends, session, corpusId, corpusData, tabType }
-        docs = R.fragment [ docsHisto, docView' TabDocs ]
-        docsHisto = histo { path, session }
-          where
-            path = { corpusId, listId: 0, limit: Nothing, tabType: TabCorpus TabDocs }
-        moreLikeFav = docView' TabMoreLikeFav
-        moreLikeTrash = docView' TabMoreLikeTrash
-        trash = docView' TabTrash
-
-type DocViewProps a =
-  ( frontends :: Frontends
-  , session :: Session
-  , corpusId :: Int
+        initialPath = { corpusId, listId: 0, limit: Nothing, tabType: TabCorpus TabDocs }
+        docView' path tabType = docView { cacheState
+                                        , corpusData
+                                        , corpusId
+                                        , frontends
+                                        -- , path
+                                        , session
+                                        , tabType }
+
+type DocViewProps a = (
+    cacheState :: R.State NT.CacheState
   , corpusData :: CorpusData
-  , tabType :: TabSubType a )
+  , corpusId   :: Int
+  , frontends  :: Frontends
+  -- , path       :: Record DT.Path
+  , session    :: Session
+  , tabType    :: TabSubType a
+  )
 
 docView :: forall a. Record (DocViewProps a) -> R.Element
 docView props = R.createElement docViewCpt props []
@@ -125,58 +146,63 @@ docView props = R.createElement docViewCpt props []
 docViewCpt :: forall a. R.Component (DocViewProps a)
 docViewCpt = R.hooksComponentWithModule thisModule "docView" cpt
   where
-    cpt {frontends, session, corpusId, corpusData: {defaultListId}, tabType} _children = do
-      pure $ DT.docViewLayout $ params tabType
-      where
-        params :: forall b. TabSubType b -> Record DT.LayoutProps
-        params TabDocs =
-          { nodeId: corpusId
-            -- ^ TODO merge nodeId and corpusId in DT
-          , chart  : H.div {} []
-          , tabType: TabCorpus TabDocs
-          , totalRecords: 4737
-          , listId: defaultListId
-          , corpusId: Just corpusId
-          , showSearch: true
-          , frontends, session }
-        params TabMoreLikeFav =
-          { nodeId: corpusId
-            -- ^ TODO merge nodeId and corpusId in DT
-          , chart  : H.div {} []
-          , tabType: TabCorpus TabMoreLikeFav
-          , totalRecords: 4737
-          , listId: defaultListId
-          , corpusId: Just corpusId
-          , showSearch: false
-          , frontends, session }
-        params TabMoreLikeTrash =
-          { nodeId: corpusId
-            -- ^ TODO merge nodeId and corpusId in DT
-          , chart  : H.div {} []
-          , tabType: TabCorpus TabMoreLikeTrash
-          , totalRecords: 4737
-          , listId: defaultListId
-          , corpusId: Just corpusId
-          , showSearch: false
-          , frontends, session }
-        params TabTrash =
-          { nodeId: corpusId
-            -- ^ TODO merge nodeId and corpusId in DT
-          , chart  : H.div {} []
-          , tabType: TabCorpus TabTrash
-          , totalRecords: 4737
-          , listId: defaultListId
-          , corpusId: Nothing
-          , showSearch: true
-          , frontends, session }
-        -- DUMMY
-        params _ =
-          { nodeId: corpusId
-            -- ^ TODO merge nodeId and corpusId in DT
-          , chart  : H.div {} []
-          , tabType: TabCorpus TabTrash
-          , totalRecords: 4737
-          , listId: defaultListId
-          , corpusId: Nothing
-          , showSearch: true
-          , frontends, session }
+    cpt props _children = do
+      pure $ DT.docViewLayout $ docViewLayoutRec props
+
+-- docViewLayoutRec :: forall a. DocViewProps a -> Record DT.LayoutProps
+docViewLayoutRec { cacheState, corpusData: { defaultListId }, corpusId, frontends, session, tabType: TabDocs } =
+  { nodeId: corpusId
+    -- ^ TODO merge nodeId and corpusId in DT
+  , cacheState
+  , chart  : H.div {} []
+  , tabType: TabCorpus TabDocs
+  , totalRecords: 4737
+  , listId: defaultListId
+  , corpusId: Just corpusId
+  , showSearch: true
+  , frontends, session }
+docViewLayoutRec { cacheState, corpusData: { defaultListId }, corpusId, frontends, session, tabType: TabMoreLikeFav } =
+  { nodeId: corpusId
+    -- ^ TODO merge nodeId and corpusId in DT
+  , cacheState
+  , chart  : H.div {} []
+  , tabType: TabCorpus TabMoreLikeFav
+  , totalRecords: 4737
+  , listId: defaultListId
+  , corpusId: Just corpusId
+  , showSearch: false
+  , frontends, session }
+docViewLayoutRec { cacheState, corpusData: { defaultListId }, corpusId, frontends, session, tabType: TabMoreLikeTrash } =
+  { nodeId: corpusId
+    -- ^ TODO merge nodeId and corpusId in DT
+  , cacheState
+  , chart  : H.div {} []
+  , tabType: TabCorpus TabMoreLikeTrash
+  , totalRecords: 4737
+  , listId: defaultListId
+  , corpusId: Just corpusId
+  , showSearch: false
+  , frontends, session }
+docViewLayoutRec { cacheState, corpusData: { defaultListId }, corpusId, frontends, session, tabType: TabTrash } =
+  { nodeId: corpusId
+    -- ^ TODO merge nodeId and corpusId in DT
+  , cacheState
+  , chart  : H.div {} []
+  , tabType: TabCorpus TabTrash
+  , totalRecords: 4737
+  , listId: defaultListId
+  , corpusId: Nothing
+  , showSearch: true
+  , frontends, session }
+-- DUMMY
+docViewLayoutRec { cacheState, corpusData: { defaultListId }, corpusId, frontends, session, tabType } =
+  { nodeId: corpusId
+    -- ^ TODO merge nodeId and corpusId in DT
+  , cacheState
+  , chart  : H.div {} []
+  , tabType: TabCorpus TabTrash
+  , totalRecords: 4737
+  , listId: defaultListId
+  , corpusId: Nothing
+  , showSearch: true
+  , frontends, session }
diff --git a/src/Gargantext/Components/Tab.purs b/src/Gargantext/Components/Tab.purs
index b79d7b02..a7d51d6a 100644
--- a/src/Gargantext/Components/Tab.purs
+++ b/src/Gargantext/Components/Tab.purs
@@ -9,9 +9,13 @@ import Reactix.DOM.HTML as H
 
 import Gargantext.Utils.Reactix as R2
 
+thisModule :: String
 thisModule = "Gargantext.Components.Tab"
 
-type TabsProps = ( tabs :: Array (Tuple String R.Element), selected :: Int )
+type TabsProps = (
+    selected :: Int
+  , tabs :: Array (Tuple String R.Element)
+  )
 
 tabs :: Record TabsProps -> R.Element
 tabs props = R.createElement tabsCpt props []
-- 
2.21.0