Commit 6cf771f9 authored by James Laver's avatar James Laver

Refactor table components to use reactix

parent 6213f074
...@@ -18,12 +18,13 @@ import Data.Tuple.Nested ((/\)) ...@@ -18,12 +18,13 @@ import Data.Tuple.Nested ((/\))
import DOM.Simple.Event as DE import DOM.Simple.Event as DE
import Effect (Effect) import Effect (Effect)
import Effect.Aff (Aff, launchAff) import Effect.Aff (Aff, launchAff)
import Effect.Uncurried (mkEffectFn1) import Effect.Class (liftEffect)
import Effect.Uncurried (EffectFn1, mkEffectFn1)
import Reactix as R import Reactix as R
import Reactix.DOM.HTML as H import Reactix.DOM.HTML as H
------------------------------------------------------------------------ ------------------------------------------------------------------------
import Gargantext.Prelude import Gargantext.Prelude
import Gargantext.Config (End(..), NodeType(..), OrderBy(..), Path(..), TabType, TabPostQuery(..), toUrl, endConfigStateful, toLink) import Gargantext.Config (Ends, NodeType(..), OrderBy(..), BackendRoute(..), TabType, TabPostQuery(..), url)
import Gargantext.Config.REST (get, put, post, deleteWithBody, delete) import Gargantext.Config.REST (get, put, post, deleteWithBody, delete)
import Gargantext.Components.Node (NodePoly(..)) import Gargantext.Components.Node (NodePoly(..))
import Gargantext.Components.Search.Types (Category(..), CategoryQuery(..), favCategory, trashCategory, decodeCategory, putCategories) import Gargantext.Components.Search.Types (Category(..), CategoryQuery(..), favCategory, trashCategory, decodeCategory, putCategories)
...@@ -38,25 +39,25 @@ type NodeID = Int ...@@ -38,25 +39,25 @@ type NodeID = Int
type TotalRecords = Int type TotalRecords = Int
type Props = type Props =
{ nodeId :: Int ( nodeId :: Int
, totalRecords :: Int , totalRecords :: Int
, chart :: R.Element , chart :: R.Element
, tabType :: TabType , tabType :: TabType
, listId :: Int , listId :: Int
, corpusId :: Maybe Int , corpusId :: Maybe Int
, showSearch :: Boolean , showSearch :: Boolean
, ends :: Ends )
-- ^ tabType is not ideal here since it is too much entangled with tabs and -- ^ tabType is not ideal here since it is too much entangled with tabs and
-- ngramtable. Let's see how this evolves. -- ngramtable. Let's see how this evolves. )
}
type PageLoaderProps = type PageLoaderProps =
{ nodeId :: Int ( nodeId :: Int
, totalRecords :: Int , totalRecords :: Int
, tabType :: TabType , tabType :: TabType
, listId :: Int , listId :: Int
, corpusId :: Maybe Int , corpusId :: Maybe Int
, query :: Query , query :: Query
} )
type LocalCategories = Map Int Category type LocalCategories = Map Int Category
type Query = String type Query = String
...@@ -119,18 +120,19 @@ instance decodeResponse :: DecodeJson Response where ...@@ -119,18 +120,19 @@ instance decodeResponse :: DecodeJson Response where
pure $ Response { cid, category: decodeCategory favorite, ngramCount, hyperdata } pure $ Response { cid, category: decodeCategory favorite, ngramCount, hyperdata }
docViewSpec :: Props -> R.Element docView :: Record Props -> R.Element
docViewSpec p = R.createElement el p [] docView p = R.createElement docViewCpt p []
docViewCpt :: R.Component Props
docViewCpt = R.hooksComponent "DocView" cpt
where where
el = R.hooksComponent "DocView" cpt
cpt p _children = do cpt p _children = do
query <- R.useState' ("" :: Query) query <- R.useState' ("" :: Query)
tableParams <- R.useState' T.initialParams tableParams <- R.useState' T.initialParams
pure $ layoutDocview query tableParams p pure $ layoutDocview query tableParams p
-- | Main layout of the Documents Tab of a Corpus -- | Main layout of the Documents Tab of a Corpus
layoutDocview :: R.State Query -> R.State T.Params -> Props -> R.Element layoutDocview :: R.State Query -> R.State T.Params -> Record Props -> R.Element
layoutDocview query tableParams@(params /\ _) p = R.createElement el p [] layoutDocview query tableParams@(params /\ _) p = R.createElement el p []
where where
el = R.hooksComponent "LayoutDocView" cpt el = R.hooksComponent "LayoutDocView" cpt
...@@ -140,9 +142,13 @@ layoutDocview query tableParams@(params /\ _) p = R.createElement el p [] ...@@ -140,9 +142,13 @@ layoutDocview query tableParams@(params /\ _) p = R.createElement el p []
[ chart [ chart
, if showSearch then searchBar query else H.div {} [] , if showSearch then searchBar query else H.div {} []
, H.div {className: "col-md-12"} , H.div {className: "col-md-12"}
[ pageLoader tableParams {nodeId, totalRecords, tabType, listId, corpusId, query: fst query} ] [ pageLoader tableParams {nodeId, totalRecords, tabType, listId, corpusId, query: fst query} ] ] ]
onClickTrashAll nodeId = mkEffectFn1 $ \_ -> do
launchAff $ deleteAllDocuments p.ends nodeId
{-, H.div {className: "col-md-1 col-md-offset-11"} {-, H.div {className: "col-md-1 col-md-offset-11"}
[ pageLoader p.ends tableParams {nodeId, totalRecords, tabType, listId, corpusId, query: fst query} ]
, H.div {className: "col-md-1 col-md-offset-11"}
[ H.button { className: "btn" [ H.button { className: "btn"
, style: {backgroundColor: "peru", color : "white", border : "white"} , style: {backgroundColor: "peru", color : "white", border : "white"}
, onClick: onClickTrashAll nodeId , onClick: onClickTrashAll nodeId
...@@ -152,11 +158,6 @@ layoutDocview query tableParams@(params /\ _) p = R.createElement el p [] ...@@ -152,11 +158,6 @@ layoutDocview query tableParams@(params /\ _) p = R.createElement el p []
] ]
] ]
-} -}
]
]
onClickTrashAll nodeId = mkEffectFn1 $ \_ -> do
launchAff $ deleteAllDocuments nodeId
searchBar :: R.State Query -> R.Element searchBar :: R.State Query -> R.Element
searchBar (query /\ setQuery) = R.createElement el {} [] searchBar (query /\ setQuery) = R.createElement el {} []
...@@ -210,12 +211,12 @@ type PageParams = { nodeId :: Int ...@@ -210,12 +211,12 @@ type PageParams = { nodeId :: Int
, query :: Query , query :: Query
, params :: T.Params} , params :: T.Params}
loadPage :: PageParams -> Aff (Array DocumentsView) loadPage :: Ends -> PageParams -> Aff (Array DocumentsView)
loadPage {nodeId, tabType, query, listId, corpusId, params: {limit, offset, orderBy}} = do loadPage ends {nodeId, tabType, query, listId, corpusId, params: {limit, offset, orderBy}} = do
logs "loading documents page: loadPage with Offset and limit" logs "loading documents page: loadPage with Offset and limit"
-- res <- get $ toUrl endConfigStateful Back (Tab tabType offset limit (convOrderBy <$> orderBy)) (Just nodeId) -- res <- get $ toUrl endConfigStateful Back (Tab tabType offset limit (convOrderBy <$> orderBy)) (Just nodeId)
let url = (toUrl endConfigStateful Back Node (Just nodeId)) <> "/table" let url2 = (url ends (NodeAPI Node (Just nodeId))) <> "/table"
res <- post url $ TabPostQuery { res <- post url2 $ TabPostQuery {
offset offset
, limit , limit
, orderBy: convOrderBy orderBy , orderBy: convOrderBy orderBy
...@@ -246,8 +247,8 @@ loadPage {nodeId, tabType, query, listId, corpusId, params: {limit, offset, orde ...@@ -246,8 +247,8 @@ loadPage {nodeId, tabType, query, listId, corpusId, params: {limit, offset, orde
convOrderBy _ = DateAsc -- TODO convOrderBy _ = DateAsc -- TODO
renderPage :: R.State T.Params -> PageLoaderProps -> Array DocumentsView -> R.Element renderPage :: Ends -> R.State T.Params -> Record PageLoaderProps -> Array DocumentsView -> R.Element
renderPage (tableParams /\ setTableParams) p res = R.createElement el p [] renderPage ends (_ /\ setTableParams) p res = R.createElement el p []
where where
el = R.hooksComponent "RenderPage" cpt el = R.hooksComponent "RenderPage" cpt
...@@ -260,61 +261,46 @@ renderPage (tableParams /\ setTableParams) p res = R.createElement el p [] ...@@ -260,61 +261,46 @@ renderPage (tableParams /\ setTableParams) p res = R.createElement el p []
cpt {nodeId, corpusId, listId, totalRecords} _children = do cpt {nodeId, corpusId, listId, totalRecords} _children = do
localCategories <- R.useState' (mempty :: LocalCategories) localCategories <- R.useState' (mempty :: LocalCategories)
pure $ T.table
pure $ R2.buff $ T.tableEltWithInitialState
(T.paramsState tableParams)
{ rows: rows localCategories { rows: rows localCategories
-- , setParams: \params -> liftEffect $ loaderDispatch (Loader.SetPath {nodeId, tabType, listId, corpusId, params, query}) -- , setParams: \params -> liftEffect $ loaderDispatch (Loader.SetPath {nodeId, tabType, listId, corpusId, params, query})
, setParams: setTableParams <<< const , setParams: setTableParams <<< const
, container: T.defaultContainer { title: "Documents" } , container: T.defaultContainer { title: "Documents" }
, colNames: , colNames
T.ColumnName <$>
[ "Favorites"
, "Trash"
, "Date"
, "Title"
, "Source"
]
, totalRecords , totalRecords
} }
where where
colNames = T.ColumnName <$> [ "Map", "Stop", "Date", "Title", "Source"]
getCategory (localCategories /\ _) {_id, category} = maybe category identity (localCategories ^. at _id) getCategory (localCategories /\ _) {_id, category} = maybe category identity (localCategories ^. at _id)
rows localCategories = (\(DocumentsView r) -> rows localCategories = row <$> res
let cat = getCategory localCategories r where
isDel = Trash == cat in row (DocumentsView r) =
{ row: map R2.scuff $ [ { row:
H.div {} [ H.div {} [ H.a { className, style, on: {click: click Favorite} } [] ]
[ H.a { className: gi cat , H.input { type: "checkbox", checked, on: {click: click Trash} }
, style: trashStyle cat
, on: {click: onClick localCategories Favorite r._id cat}
} []
]
, H.input { type: "checkbox"
, defaultChecked: isDel
, on: {click: onClick localCategories Trash r._id cat}
}
-- TODO show date: Year-Month-Day only -- TODO show date: Year-Month-Day only
, H.div { style: trashStyle cat } [ H.text (show r.date) ] , H.div { style } [ R2.showText r.date ]
, H.a { href: toLink $ (corpusDocument corpusId) listId r._id , H.div { style } [ H.text r.source ]
, style: trashStyle cat
, target: "_blank"
} [ H.text r.title ]
, H.div { style: trashStyle cat} [ H.text r.source ]
] ]
, delete: true , delete: true }
}) <$> res where
cat = getCategory localCategories r
click cat2 = onClick localCategories cat2 r._id cat
checked = Trash == cat
style = trashStyle cat
className = gi cat
onClick (_ /\ setLocalCategories) catType nid cat = \_-> do onClick (_ /\ setLocalCategories) catType nid cat = \_-> do
let newCat = if (catType == Favorite) then (favCategory cat) else (trashCategory cat) let newCat = if (catType == Favorite) then (favCategory cat) else (trashCategory cat)
setLocalCategories $ insert nid newCat setLocalCategories $ insert nid newCat
void $ launchAff $ putCategories nodeId $ CategoryQuery {nodeIds: [nid], category: newCat} void $ launchAff $ putCategories ends nodeId $ CategoryQuery {nodeIds: [nid], category: newCat}
pageLoader :: R.State T.Params -> PageLoaderProps -> R.Element pageLoader :: Ends -> R.State T.Params -> Record PageLoaderProps -> R.Element
pageLoader tableParams@(pageParams /\ _) p = R.createElement el p [] pageLoader ends tableParams@(pageParams /\ _) p = R.createElement el p []
where where
el = R.hooksComponent "PageLoader" cpt el = R.hooksComponent "PageLoader" cpt
cpt p@{nodeId, listId, corpusId, tabType, query} _children = do cpt p@{nodeId, listId, corpusId, tabType, query} _children = do
useLoader {nodeId, listId, corpusId, tabType, query, params: pageParams} loadPage $ \{loaded} -> useLoader {nodeId, listId, corpusId, tabType, query, params: pageParams} (loadPage ends) $
renderPage tableParams p loaded \loaded -> renderPage ends tableParams p loaded
--------------------------------------------------------- ---------------------------------------------------------
sampleData' :: DocumentsView sampleData' :: DocumentsView
...@@ -358,7 +344,6 @@ searchResults :: SearchQuery -> Aff Int ...@@ -358,7 +344,6 @@ searchResults :: SearchQuery -> Aff Int
searchResults squery = post "http://localhost:8008/count" unit searchResults squery = post "http://localhost:8008/count" unit
-- TODO -- TODO
newtype CategoryQuery = CategoryQuery { newtype CategoryQuery = CategoryQuery {
nodeIds :: Array Int nodeIds :: Array Int
, category :: Category , category :: Category
...@@ -376,11 +361,11 @@ categoryUrl nodeId = toUrl endConfigStateful Back Node (Just nodeId) <> "/catego ...@@ -376,11 +361,11 @@ categoryUrl nodeId = toUrl endConfigStateful Back Node (Just nodeId) <> "/catego
putCategories :: Int -> CategoryQuery -> Aff (Array Int) putCategories :: Int -> CategoryQuery -> Aff (Array Int)
putCategories nodeId = put $ categoryUrl nodeId putCategories nodeId = put $ categoryUrl nodeId
documentsUrl :: Int -> String documentsUrl :: Ends -> Int -> String
documentsUrl nodeId = toUrl endConfigStateful Back Node (Just nodeId) <> "/documents" documentsUrl ends nodeId = url ends (NodeAPI Node (Just nodeId)) <> "/documents"
deleteAllDocuments :: Int -> Aff (Array Int) deleteAllDocuments :: Ends -> Int -> Aff (Array Int)
deleteAllDocuments nodeId = delete $ documentsUrl nodeId deleteAllDocuments ends = delete <<< documentsUrl ends
-- TODO: not optimal but Data.Set lacks some function (Set.alter) -- TODO: not optimal but Data.Set lacks some function (Set.alter)
toggleSet :: forall a. Ord a => a -> Set a -> Set a toggleSet :: forall a. Ord a => a -> Set a -> Set a
......
...@@ -12,24 +12,25 @@ import Data.Generic.Rep.Show (genericShow) ...@@ -12,24 +12,25 @@ import Data.Generic.Rep.Show (genericShow)
import Data.Maybe (Maybe(..)) import Data.Maybe (Maybe(..))
import Data.Set (Set) import Data.Set (Set)
import Data.Set as Set import Data.Set as Set
import Data.Tuple (fst)
import Data.Tuple.Nested ((/\))
import Effect (Effect) import Effect (Effect)
import Effect.Aff (Aff) import Effect.Aff (Aff, launchAff_)
import Effect.Class (liftEffect) import Effect.Class (liftEffect)
import React as React import Reactix as R
import React (ReactClass, ReactElement, Children) import Reactix.DOM.HTML as H
------------------------------------------------------------------------ ------------------------------------------------------------------------
import Gargantext.Prelude import Gargantext.Prelude
import Gargantext.Config (End(..), NodeType(..), OrderBy(..), Path(..), TabType, toUrl, endConfigStateful) import Gargantext.Config (Ends, NodeType(..), OrderBy(..), NodePath(..), BackendRoute(..), TabType, url)
import Gargantext.Config.REST (put, post, deleteWithBody) import Gargantext.Config.REST (put, post, deleteWithBody)
import Gargantext.Components.Loader as Loader import Gargantext.Hooks.Loader (useLoader)
import Gargantext.Components.Search.Types (Category(..), CategoryQuery(..), favCategory, decodeCategory, putCategories) import Gargantext.Components.Search.Types (Category(..), CategoryQuery(..), favCategory, trashCategory, decodeCategory, putCategories)
import Gargantext.Components.Table as T import Gargantext.Components.Table as T
import Gargantext.Router as Router import Gargantext.Router as Router
import Gargantext.Utils (toggleSet) import Gargantext.Utils (toggleSet)
import Gargantext.Utils.DecodeMaybe ((.|)) import Gargantext.Utils.DecodeMaybe ((.|))
import React.DOM (a, br', button, div, i, input, p, text, span) import Gargantext.Utils.Reactix as R2
import React.DOM.Props (_type, className, href, onClick, style, checked, target)
import Thermite (PerformAction, Render, Spec, defaultPerformAction, modifyState_, simpleSpec, hideState)
------------------------------------------------------------------------ ------------------------------------------------------------------------
type NodeID = Int type NodeID = Int
...@@ -40,9 +41,7 @@ type TotalRecords = Int ...@@ -40,9 +41,7 @@ type TotalRecords = Int
-- This searches for documents with "machine learning" or "artificial intelligence" -- This searches for documents with "machine learning" or "artificial intelligence"
type TextQuery = Array (Array String) type TextQuery = Array (Array String)
newtype SearchQuery = SearchQuery newtype SearchQuery = SearchQuery { query :: TextQuery }
{ query :: TextQuery
}
instance encodeJsonSearchQuery :: EncodeJson SearchQuery where instance encodeJsonSearchQuery :: EncodeJson SearchQuery where
encodeJson (SearchQuery post) encodeJson (SearchQuery post)
...@@ -58,42 +57,30 @@ instance decodeSearchResults :: DecodeJson SearchResults where ...@@ -58,42 +57,30 @@ instance decodeSearchResults :: DecodeJson SearchResults where
pure $ SearchResults {results} pure $ SearchResults {results}
type Props = type Props =
{ nodeId :: Int ( nodeId :: Int
, listId :: Int , listId :: Int
, query :: TextQuery , query :: TextQuery
, totalRecords :: Int , totalRecords :: Int
, chart :: ReactElement , chart :: R.Element
, container :: T.TableContainerProps -> Array ReactElement , container :: Record T.TableContainerProps -> R.Element
} , ends :: Ends
)
type State = -- | Tracks the ids of documents to delete and that have been deleted
{ documentIdsToDelete :: Set Int type Deletions = { pending :: Set Int, deleted :: Set Int }
, documentIdsDeleted :: Set Int
}
initialState :: State initialDeletions :: Deletions
initialState = initialDeletions = { pending: mempty, deleted: mempty }
{ documentIdsToDelete: mempty
, documentIdsDeleted: mempty
}
data Action newtype Pair = Pair { id :: Int, label :: String }
= MarkCategory Category (Array Int)
| ToggleDocumentToDelete Int
| TrashDocuments
newtype Pair = Pair
{ id :: Int
, label :: String
}
derive instance genericPair :: Generic Pair _ derive instance genericPair :: Generic Pair _
instance showPair :: Show Pair where instance showPair :: Show Pair where
show = genericShow show = genericShow
newtype DocumentsView newtype DocumentsView =
= DocumentsView DocumentsView
{ id :: Int { id :: Int
, date :: String , date :: String
, title :: String , title :: String
...@@ -120,7 +107,6 @@ newtype Response = Response ...@@ -120,7 +107,6 @@ newtype Response = Response
-- , pairs :: Array Pair -- , pairs :: Array Pair
} }
newtype Hyperdata = Hyperdata newtype Hyperdata = Hyperdata
{ title :: String { title :: String
, source :: String , source :: String
...@@ -133,7 +119,6 @@ newtype Hyperdata = Hyperdata ...@@ -133,7 +119,6 @@ newtype Hyperdata = Hyperdata
-- source <- obj .: "source" -- source <- obj .: "source"
-- pure $ Hyperdata { title,source } -- pure $ Hyperdata { title,source }
instance decodePair :: DecodeJson Pair where instance decodePair :: DecodeJson Pair where
decodeJson json = do decodeJson json = do
obj <- decodeJson json obj <- decodeJson json
...@@ -172,252 +157,180 @@ instance decodeResponse :: DecodeJson Response where ...@@ -172,252 +157,180 @@ instance decodeResponse :: DecodeJson Response where
let ngramCount = 1 let ngramCount = 1
pure $ Response { id, created, hyperdata, category: decodeCategory favorite, ngramCount} pure $ Response { id, created, hyperdata, category: decodeCategory favorite, ngramCount}
-- | Filter
-- TODO: unused
filterSpec :: forall state props action. Spec state props action
filterSpec = simpleSpec defaultPerformAction render
where
render d p s c = [] {-[div [ className "col-md-2", style {textAlign : "center", marginLeft : "0px", paddingLeft : "0px"}] [ text " Filter "
, input [className "form-control", placeholder "Filter here"]
]]
-}
docViewSpec :: Spec {} Props Void
docViewSpec = hideState (const initialState) layoutDocviewGraph
-- | Main layout of the Documents Tab of a Corpus -- | Main layout of the Documents Tab of a Corpus
layoutDocview :: Spec State Props Action docView :: Record Props -> R.Element
layoutDocview = simpleSpec performAction render docView props = R.createElement docViewCpt props []
where
performAction :: PerformAction State Props Action
performAction (MarkCategory category nids) {nodeId} _ =
void $ lift $ putCategories nodeId $ CategoryQuery {nodeIds: nids, category: favCategory category}
--TODO add array of delete rows here
performAction (ToggleDocumentToDelete nid) _ _ =
modifyState_ \state -> state {documentIdsToDelete = toggleSet nid state.documentIdsToDelete}
performAction TrashDocuments {nodeId} {documentIdsToDelete} = do
void $ lift $ deleteDocuments nodeId (DeleteDocumentQuery {documents: Set.toUnfoldable documentIdsToDelete})
modifyState_ \{documentIdsToDelete, documentIdsDeleted} ->
{ documentIdsToDelete: mempty
, documentIdsDeleted: documentIdsDeleted <> documentIdsToDelete
}
render :: Render State Props Action docViewCpt :: R.Component Props
render dispatch {nodeId, listId, query, totalRecords, chart, container} deletionState _ = docViewCpt = R.hooksComponent "G.C.FacetsTable.DocView" cpt
[ {- br' where
, div [ style {textAlign : "center"}] [ text " Filter " cpt {ends, nodeId, listId, query, totalRecords, chart, container} _ = do
, input [className "form-control", style {width : "120px", display : "inline-block"}, placeholder "Filter here"] deletions <- R.useState' initialDeletions
] path <- R.useState' $ initialPagePath {nodeId, listId, query, ends}
, p [] [text ""] pure $ H.div { className: "container1" }
, br' [ H.div { className: "row" }
-}
div [className "container1"]
[ div [className "row"]
[ chart [ chart
, div [className "col-md-12"] , H.div { className: "col-md-12" }
[ pageLoader [ pageLayout { deletions, totalRecords, container, ends, path } ]
{ path: initialPageParams {nodeId, listId, query} , H.div { className: "col-md-12" }
, totalRecords [ H.button { style: buttonStyle, on: { click: trashClick deletions } }
, deletionState [ H.i { className: "glyphitem glyphicon glyphicon-trash"
, dispatch , style: { marginRight : "9px" }} []
, container , H.text "Delete document!" ] ] ] ]
}
]
, div [className "col-md-12"]
[ button [ style {backgroundColor: "peru", padding : "9px", color : "white", border : "white", float: "right"}
, onClick $ (\_ -> dispatch TrashDocuments)
]
[ i [className "glyphitem glyphicon glyphicon-trash", style {marginRight : "9px"}] []
, text "Trash it !"
]
]
]
]
]
layoutDocviewGraph :: Spec State Props Action
layoutDocviewGraph = simpleSpec performAction render
where where
performAction :: PerformAction State Props Action buttonStyle =
performAction (MarkCategory category nids) {nodeId} _ = { backgroundColor: "peru", padding: "9px", color: "white"
void $ lift $ putCategories nodeId $ CategoryQuery {nodeIds: nids, category: favCategory category} , border: "white", float: "right" }
--TODO add array of delete rows here trashClick deletions _ = performDeletions ends nodeId deletions
performAction (ToggleDocumentToDelete nid) _ _ =
modifyState_ \state -> state {documentIdsToDelete = toggleSet nid state.documentIdsToDelete} performDeletions :: Ends -> Int -> R.State Deletions -> Effect Unit
performAction TrashDocuments {nodeId} {documentIdsToDelete} = do performDeletions ends nodeId (deletions /\ setDeletions) =
void $ lift $ deleteDocuments nodeId (DeleteDocumentQuery {documents: Set.toUnfoldable documentIdsToDelete}) launchAff_ call *> setDeletions del
modifyState_ \{documentIdsToDelete, documentIdsDeleted} -> where
{ documentIdsToDelete: mempty q = {documents: Set.toUnfoldable deletions.pending}
, documentIdsDeleted: documentIdsDeleted <> documentIdsToDelete call = deleteDocuments ends nodeId (DeleteDocumentQuery q)
} del {pending, deleted} = {pending: mempty, deleted: deleted <> pending}
render :: Render State Props Action
render dispatch {nodeId, listId, query, totalRecords, chart, container} deletionState _ =
[ br'
, p [] [text ""] -- markCategory :: Ends -> NodeID -> _ -> Array NodeID -> Effect Unit
, br' markCategory ends nodeId category nids =
, div [className "container-fluid"] void $ launchAff_ $putCategories ends nodeId (CategoryQuery q)
[ div [className "row"] where -- TODO add array of delete rows here
[ chart q = {nodeIds: nids, category: favCategory category}
, div [className "col-md-12"]
[ pageLoader
{ path: initialPageParams {nodeId, listId, query}
, totalRecords
, deletionState
, dispatch
, container
}
, button [ style {backgroundColor: "peru", padding : "9px", color : "white", border : "white", float: "right"}
, onClick $ (\_ -> dispatch TrashDocuments)
]
[ i [className "glyphitem glyphicon glyphicon-trash", style {marginRight : "9px"}] []
, text "Trash it !"
]
]
] togglePendingDeletion :: R.State Deletions -> NodeID -> Effect Unit
] togglePendingDeletion (_ /\ setDeletions) nid = setDeletions setter
] where setter deletions@{pending} = deletions { pending = toggleSet nid pending }
docViewGraph :: Record Props -> R.Element
docViewGraph props = R.createElement docViewCpt props []
docViewGraphCpt :: R.Component Props
docViewGraphCpt = R.hooksComponent "FacetsDocViewGraph" cpt
where
cpt {ends, nodeId, listId, query, totalRecords, chart, container} _ = do
deletions <- R.useState' initialDeletions
let buttonStyle = { backgroundColor: "peru", padding : "9px"
, color : "white", border : "white", float: "right"}
let performClick = \_ -> performDeletions ends nodeId deletions
path <- R.useState' $ initialPagePath { nodeId, listId, query, ends }
pure $ R.fragment
[ H.br {}
, H.p {} [ H.text "" ]
, H.br {}
, H.div { className: "container-fluid" }
[ H.div { className: "row" }
[ chart
, H.div { className: "col-md-12" }
[ pageLayout { totalRecords, deletions, container, ends, path }
, H.button { style: buttonStyle, on: { click: performClick } }
[ H.i { className: "glyphitem glyphicon glyphicon-trash"
, style: { marginRight : "9px" } } []
, H.text "Delete document!" ] ] ] ] ]
type PageParams = {nodeId :: Int, listId :: Int, query :: TextQuery, params :: T.Params} type PagePath = {nodeId :: Int, listId :: Int, query :: TextQuery, params :: T.Params, ends :: Ends}
initialPageParams :: {nodeId :: Int, listId :: Int, query :: TextQuery} -> PageParams initialPagePath :: {ends :: Ends, nodeId :: Int, listId :: Int, query :: TextQuery} -> PagePath
initialPageParams {nodeId, listId, query} = {nodeId, listId, query, params: T.initialParams} initialPagePath {ends, nodeId, listId, query} = {ends, nodeId, listId, query, params: T.initialParams}
loadPage :: PageParams -> Aff (Array DocumentsView) loadPage :: PagePath -> Aff (Array DocumentsView)
loadPage {nodeId, listId, query, params: {limit, offset, orderBy}} = do loadPage {ends, nodeId, listId, query, params: {limit, offset, orderBy}} = do
logs "loading documents page: loadPage with Offset and limit" logs "loading documents page: loadPage with Offset and limit"
let url = toUrl endConfigStateful Back (Search { listId, offset, limit, orderBy: convOrderBy <$> orderBy }) (Just nodeId) let url2 = url ends $ Search { listId, offset, limit, orderBy: convOrderBy <$> orderBy } (Just nodeId)
SearchResults res <- post url $ SearchQuery {query} SearchResults res <- post url2 $ SearchQuery {query}
pure $ res2corpus <$> res.results pure $ res2corpus <$> res.results
where where
res2corpus :: Response -> DocumentsView res2corpus :: Response -> DocumentsView
res2corpus (Response { id, created: date, ngramCount: score res2corpus (Response { id, created: date, ngramCount: score, category
, hyperdata: Hyperdata {title, source} , hyperdata: Hyperdata {title, source} }) =
, category DocumentsView { id, date, title, source, score, category, pairs: [], delete: false }
}) =
DocumentsView
{ id
, date
, title
, source
, score
, pairs: []
, delete: false
, category
}
convOrderBy (T.ASC (T.ColumnName "Date")) = DateAsc convOrderBy (T.ASC (T.ColumnName "Date")) = DateAsc
convOrderBy (T.DESC (T.ColumnName "Date")) = DateDesc convOrderBy (T.DESC (T.ColumnName "Date")) = DateDesc
convOrderBy (T.ASC (T.ColumnName "Title")) = TitleAsc convOrderBy (T.ASC (T.ColumnName "Title")) = TitleAsc
convOrderBy (T.DESC (T.ColumnName "Title")) = TitleDesc convOrderBy (T.DESC (T.ColumnName "Title")) = TitleDesc
convOrderBy (T.ASC (T.ColumnName "Source")) = SourceAsc convOrderBy (T.ASC (T.ColumnName "Source")) = SourceAsc
convOrderBy (T.DESC (T.ColumnName "Source")) = SourceDesc convOrderBy (T.DESC (T.ColumnName "Source")) = SourceDesc
convOrderBy _ = DateAsc -- TODO convOrderBy _ = DateAsc -- TODO
type PageLoaderProps row = type PageLayoutProps =
{ path :: PageParams ( totalRecords :: Int
, totalRecords :: Int , deletions :: R.State Deletions
, dispatch :: Action -> Effect Unit , container :: Record T.TableContainerProps -> R.Element
, deletionState :: State , ends :: Ends
, container :: T.TableContainerProps -> Array ReactElement , path :: R.State PagePath
| row )
}
renderPage :: forall props path. type PageProps = ( documents :: Array DocumentsView | PageLayoutProps )
Render (Loader.State {nodeId :: Int, listId :: Int, query :: TextQuery | path} (Array DocumentsView))
{ totalRecords :: Int -- | Loads and renders a page
, dispatch :: Action -> Effect Unit pageLayout :: Record PageLayoutProps -> R.Element
, deletionState :: State pageLayout props = R.createElement pageLayoutCpt props []
, container :: T.TableContainerProps -> Array ReactElement
| props pageLayoutCpt :: R.Component PageLayoutProps
} pageLayoutCpt = R.hooksComponent "G.C.FacetsTable.PageLayout" cpt
(Loader.Action PageParams) where
renderPage _ _ {loaded: Nothing} _ = [] -- TODO loading spinner cpt {totalRecords, deletions, container, ends, path} _ = do
renderPage loaderDispatch { totalRecords, dispatch, container useLoader (fst path) loadPage $ \documents ->
, deletionState: {documentIdsToDelete, documentIdsDeleted}} page {totalRecords, deletions, container, ends, path, documents}
{currentPath: {nodeId, listId, query}, loaded: Just res} _ =
[ T.tableElt page :: Record PageProps -> R.Element
{ rows page props = R.createElement pageCpt props []
, setParams: \params -> liftEffect $ loaderDispatch (Loader.SetPath {nodeId, listId, query, params})
, container pageCpt :: R.Component PageProps
, colNames: pageCpt = R.staticComponent "G.C.FacetsTable.Page" cpt
T.ColumnName <$> where
[ "" cpt {totalRecords, container, deletions, documents, ends, path: path@({nodeId, listId, query} /\ setPath)} _ = do
, "Date" T.table { rows, container, colNames, totalRecords, setParams }
, "Title"
, "Source"
, "Authors"
, "Delete"
]
, totalRecords
}
]
where where
setParams params = setPath (_ {params = params})
colNames = T.ColumnName <$> [ "", "Date", "Title", "Source", "Authors", "Delete" ]
-- TODO: how to interprete other scores? -- TODO: how to interprete other scores?
gi Favorite = "glyphicon glyphicon-star-empty" gi Favorite = "glyphicon glyphicon-star-empty"
gi _ = "glyphicon glyphicon-star" gi _ = "glyphicon glyphicon-star"
isChecked id = Set.member id documentIdsToDelete isChecked id = Set.member id (fst deletions).pending
isDeleted (DocumentsView {id}) = Set.member id documentIdsDeleted isDeleted (DocumentsView {id}) = Set.member id (fst deletions).deleted
pairUrl (Pair {id,label}) pairUrl (Pair {id,label})
| id > 1 = [a [href (toUrl endConfigStateful Front NodeContact (Just id)), target "blank"] [text label]] | id > 1 = H.a { href, target: "blank" } [ H.text label ]
| otherwise = [text label] where href = url ends $ NodePath NodeContact (Just id)
comma = span [] [text ", "] | otherwise = H.text label
rows = (\(DocumentsView {id,score,title,source,date,pairs,delete,category}) -> comma = H.span {} [ H.text ", " ]
let rows = row <$> filter (not <<< isDeleted) documents
strikeIfDeleted where
| delete = [style {textDecoration : "line-through"}] row (DocumentsView {id,score,title,source,date,pairs,delete,category}) =
| otherwise = []
in
{ row: { row:
[ div [] [ H.div {}
[ a [ className $ gi category [ H.a { className, on: {click: markClick} } []
, onClick $ const $ dispatch $ MarkCategory category [id]
] []
]
-- TODO show date: Year-Month-Day only -- TODO show date: Year-Month-Day only
, div strikeIfDeleted [text date] , maybeStricken [ H.text date ]
, a (strikeIfDeleted <> [ href $ toLink endConfigStateful $ Router.Document listId id , maybeStricken [ H.text source ]
, target "blank"]) -- , maybeStricken $ intercalate [comma] (pairUrl <$> pairs)
[ text title ] , H.input { type: "checkbox", checked: isChecked id, on: { click: toggleClick } }
, div strikeIfDeleted [text source] ] ]
, div strikeIfDeleted $ intercalate [comma] $ pairUrl <$> pairs , delete: true }
, input [ _type "checkbox" where
, checked (isChecked id) markClick _ = markCategory ends nodeId category [id]
, onClick $ const $ dispatch $ ToggleDocumentToDelete id] toggleClick _ = togglePendingDeletion deletions id
] className = gi category
, delete: true maybeStricken
}) <$> filter (not <<< isDeleted) res | delete = H.div { style: { textDecoration: "line-through" } }
| otherwise = H.div {}
pageLoaderClass :: ReactClass (PageLoaderProps (children :: Children))
pageLoaderClass = Loader.createLoaderClass' "PageLoader" loadPage renderPage
pageLoader :: PageLoaderProps () -> ReactElement
pageLoader props = React.createElement pageLoaderClass props []
---------------------------------------------------------
newtype DeleteDocumentQuery = DeleteDocumentQuery ---------------------------------------------------------
{
documents :: Array Int
}
newtype DeleteDocumentQuery = DeleteDocumentQuery { documents :: Array Int }
instance encodeJsonDDQuery :: EncodeJson DeleteDocumentQuery where instance encodeJsonDDQuery :: EncodeJson DeleteDocumentQuery where
encodeJson (DeleteDocumentQuery post) encodeJson (DeleteDocumentQuery post) =
= "documents" := post.documents "documents" := post.documents ~> jsonEmptyObject
~> jsonEmptyObject
putFavorites :: Int -> FavoriteQuery -> Aff (Array Int) putFavorites :: Ends -> Int -> FavoriteQuery -> Aff (Array Int)
putFavorites nodeId = put (toUrl endConfigStateful Back Node (Just nodeId) <> "/favorites") putFavorites ends nodeId = put to
where to = url endst (NodeAPI Node (Just nodeId)) <> "/favorites"
deleteFavorites :: Int -> FavoriteQuery -> Aff (Array Int) deleteFavorites :: Int -> FavoriteQuery -> Aff (Array Int)
deleteFavorites nodeId = deleteWithBody (toUrl endConfigStateful Back Node (Just nodeId) <> "/favorites") deleteFavorites nodeId = deleteWithBody to
where to = url ends (NodeAPI Node (Just nodeId)) <> "/favorites"
deleteDocuments :: Int -> DeleteDocumentQuery -> Aff (Array Int) deleteDocuments :: Ends -> Int -> DeleteDocumentQuery -> Aff (Array Int)
deleteDocuments nodeId = deleteWithBody (toUrl endConfigStateful Back Node (Just nodeId) <> "/documents") deleteDocuments ends nodeId = deleteWithBody to
where to = url ends (NodeAPI Node (Just nodeId)) <> "/documents"
module Gargantext.Components.NgramsTable module Gargantext.Components.NgramsTable where
( Action
, MainNgramsTableProps
, initialState
, mainNgramsTableSpec
, ngramsTableClass
, ngramsTableSpec
, termStyle
)
where
import Data.Array as A import Data.Array as A
import Data.Lens (to, view, (%~), (.~), (^.), (^..)) import Data.Lens (to, view, (%~), (.~), (^.), (^..))
...@@ -24,7 +15,10 @@ import Data.Monoid.Additive (Additive(..)) ...@@ -24,7 +15,10 @@ import Data.Monoid.Additive (Additive(..))
import Data.Ord.Down (Down(..)) import Data.Ord.Down (Down(..))
import Data.Symbol (SProxy(..)) import Data.Symbol (SProxy(..))
import Data.Tuple (Tuple(..), snd) import Data.Tuple (Tuple(..), snd)
import Data.Tuple.Nested ((/\))
import Effect (Effect) import Effect (Effect)
import Reactix as R
import Reactix.DOM.HTML as H
import React (ReactElement) import React (ReactElement)
import React.DOM (a, button, div, h2, i, input, li, option, p, select, span, table, tbody, text, thead, ul) import React.DOM (a, button, div, h2, i, input, li, option, p, select, span, table, tbody, text, thead, ul)
import React.DOM.Props (_id, _type, checked, className, name, onChange, onClick, onInput, placeholder, style, value) import React.DOM.Props (_id, _type, checked, className, name, onChange, onClick, onInput, placeholder, style, value)
...@@ -32,11 +26,11 @@ import React.DOM.Props as DOM ...@@ -32,11 +26,11 @@ import React.DOM.Props as DOM
import Thermite (PerformAction, Render, Spec, defaultPerformAction, modifyState_, simpleSpec, createClass) import Thermite (PerformAction, Render, Spec, defaultPerformAction, modifyState_, simpleSpec, createClass)
import Gargantext.Types (TermList(..), readTermList, readTermSize, termLists, termSizes) import Gargantext.Types (TermList(..), readTermList, readTermSize, termLists, termSizes)
import Gargantext.Config (OrderBy(..), TabType, CTabNgramType(..)) import Gargantext.Config (Ends, OrderBy(..), TabType, CTabNgramType(..))
import Gargantext.Components.AutoUpdate (autoUpdateElt) import Gargantext.Components.AutoUpdate (autoUpdateElt)
import Gargantext.Components.Table as T import Gargantext.Components.Table as T
import Gargantext.Prelude import Gargantext.Prelude
import Gargantext.Components.Loader as Loader import Gargantext.Hooks.Loader (useLoader, useLoader2)
import Gargantext.Components.NgramsTable.Core import Gargantext.Components.NgramsTable.Core
import Gargantext.Utils.Reactix as R2 import Gargantext.Utils.Reactix as R2
...@@ -52,9 +46,8 @@ type State = ...@@ -52,9 +46,8 @@ type State =
_ngramsChildren = prop (SProxy :: SProxy "ngramsChildren") _ngramsChildren = prop (SProxy :: SProxy "ngramsChildren")
initialState :: forall props. { loaded :: VersionedNgramsTable | props } initialState :: VersionedNgramsTable -> State
-> State initialState (Versioned {version}) =
initialState {loaded: Versioned {version}} =
{ ngramsTablePatch: mempty { ngramsTablePatch: mempty
, ngramsVersion: version , ngramsVersion: version
, ngramsParent: Nothing , ngramsParent: Nothing
...@@ -78,86 +71,66 @@ data Action ...@@ -78,86 +71,66 @@ data Action
type Dispatch = Action -> Effect Unit type Dispatch = Action -> Effect Unit
type LoaderAction = Loader.Action PageParams
type LoaderDispatch = LoaderAction -> Effect Unit
tableContainer :: { pageParams :: PageParams tableContainer :: { pageParams :: PageParams
, dispatch :: Dispatch , dispatch :: Dispatch
, loaderDispatch :: LoaderDispatch , setPath :: R2.Setter PageParams
, ngramsParent :: Maybe NgramsTerm , ngramsParent :: Maybe NgramsTerm
, ngramsChildren :: Map NgramsTerm Boolean , ngramsChildren :: Map NgramsTerm Boolean
, ngramsTable :: NgramsTable , ngramsTable :: NgramsTable
} }
-> T.TableContainerProps -> Array ReactElement -> Record T.TableContainerProps -> R.Element
tableContainer { pageParams tableContainer { pageParams
, dispatch , dispatch
, loaderDispatch , setPath
, ngramsParent , ngramsParent
, ngramsChildren , ngramsChildren
, ngramsTable: ngramsTableCache , ngramsTable: ngramsTableCache
} props = } props =
[ div [className "container-fluid"] H.div {className: "container-fluid"}
[ div [className "jumbotron1"] [ H.div {className: "jumbotron1"}
[ div [className "row"] [ H.div {className: "row"}
[ div [className "panel panel-default"] [ H.div {className: "panel panel-default"}
[ div [className "panel-heading"] [ H.div {className: "panel-heading"}
[ h2 [className "panel-title", style {textAlign : "center"}] [ H.h2 {className: "panel-title", style: {textAlign : "center"}}
[ span [className "glyphicon glyphicon-hand-down"] [] [ H.span {className: "glyphicon glyphicon-hand-down"} []
, text "Extracted Terms" , H.text "Extracted Terms"
]
, div [className "row"]
[
{-div [className "savediv pull-left col-md-2", style { marginTop :"35px"}]
[ button [_id "ImportListOrSaveAll", className "btn btn-warning", style {fontSize : "120%"}]
[ text "Import a Termlist" ]
] ]
,-} , H.div {className: "row"}
div [className "col-md-3", style {marginTop : "6px"}] [ H.div {className: "col-md-3", style: {marginTop: "6px"}}
[ input [ className "form-control " [ H.input { className: "form-control"
, name "search", placeholder "Search" , name: "search"
, _type "value" , placeholder: "Search"
, value pageParams.searchQuery , type: "value"
, onInput \e -> setSearchQuery (R2.unsafeEventValue e) , value: pageParams.searchQuery
] , on: {input: \e -> setSearchQuery (R2.unsafeEventValue e)}}
, div [] ( , H.div {} (
if A.null props.tableBody && pageParams.searchQuery /= "" then [ if A.null props.tableBody && pageParams.searchQuery /= "" then [
button [ className "btn btn-primary" H.button { className: "btn btn-primary"
, onClick $ const $ dispatch $ AddNewNgram pageParams.searchQuery , on: {click: const $ dispatch $ AddNewNgram pageParams.searchQuery}}
] [text $ "Add " <> pageParams.searchQuery] [ H.text ("Add " <> pageParams.searchQuery) ]
] else [] ] else [])]
) , H.div {className: "col-md-2", style: {marginTop : "6px"}}
] [ H.li {className: " list-group-item"}
, div [className "col-md-2", style {marginTop : "6px"}] [ R2.select { id: "picklistmenu"
[ li [className " list-group-item"] , className: "form-control custom-select"
[ select [ _id "picklistmenu" , value: (maybe "" show pageParams.termListFilter)
, className "form-control custom-select" , on: {change: (\e -> setTermListFilter $ readTermList $ R2.unsafeEventValue e)}}
, value (maybe "" show pageParams.termListFilter) (map optps1 termLists)]]
, onChange (\e -> setTermListFilter $ readTermList $ R2.unsafeEventValue e) , H.div {className: "col-md-2", style: {marginTop : "6px"}}
] $ map optps1 termLists [ H.li {className: "list-group-item"}
] [ R2.select {id: "picktermtype"
] , className: "form-control custom-select"
, div [className "col-md-2", style {marginTop : "6px"}] , value: (maybe "" show pageParams.termSizeFilter)
[ li [className "list-group-item"] , on: {change: (\e -> setTermSizeFilter $ readTermSize $ R2.unsafeEventValue e)}}
[ select [ _id "picktermtype" (map optps1 termSizes)]]
, className "form-control custom-select" , H.div {className: "col-md-4", style: {marginTop : "6px", marginBottom : "1px"}}
, value (maybe "" show pageParams.termSizeFilter) [ H.li {className: " list-group-item"}
, onChange (\e -> setTermSizeFilter $ readTermSize $ R2.unsafeEventValue e) [ props.pageSizeDescription
] $ map optps1 termSizes
]
]
, div [className "col-md-4", style {marginTop : "6px", marginBottom : "1px"}]
[ li [className " list-group-item"] [ props.pageSizeDescription
, props.pageSizeControl , props.pageSizeControl
, text " items / " , H.text " items / "
, props.paginationLinks , props.paginationLinks]]]]
] , H.div {}
--, li [className " list-group-item"] [ props.pageSizeControl ] (maybe [] (\ngrams ->
]
]
]
, div [] (maybe [] (\ngrams ->
let let
ngramsTable = ngramsTable =
ngramsTableCache # at ngrams ngramsTableCache # at ngrams
...@@ -170,24 +143,17 @@ tableContainer { pageParams ...@@ -170,24 +143,17 @@ tableContainer { pageParams
ngramsClick _ = Nothing ngramsClick _ = Nothing
ngramsEdit _ = Nothing ngramsEdit _ = Nothing
in in
[ p[] [text $ "Editing " <> ngrams] [ H.p {} [H.text $ "Editing " <> ngrams]
, renderNgramsTree { ngramsTable, ngrams, ngramsStyle: [], ngramsClick, ngramsEdit } , R2.buff $ renderNgramsTree { ngramsTable, ngrams, ngramsStyle: [], ngramsClick, ngramsEdit }
, button [className "btn btn-primary", onClick $ const $ dispatch $ AddTermChildren] [text "Save"] , H.button {className: "btn btn-primary", on: {click: (const $ dispatch $ AddTermChildren)}} [H.text "Save"]
, button [className "btn btn-secondary", onClick $ const $ dispatch $ SetParentResetChildren Nothing] [text "Cancel"] , H.button {className: "btn btn-secondary", on: {click: (const $ dispatch $ SetParentResetChildren Nothing)}} [H.text "Cancel"]
]) ngramsParent) ]) ngramsParent)
, div [ _id "terms_table", className "panel-body" ] , H.div {id: "terms_table", className: "panel-body"}
[ table [ className "table able" ] [ H.table {className: "table able"}
[ thead [ className "tableHeader"] [props.tableHead] [ H.thead {className: "tableHeader"} [props.tableHead]
, tbody [] props.tableBody , H.tbody {} props.tableBody]]]]]]
]
]
]
]
]
]
]
where where
setPageParams f = loaderDispatch $ Loader.SetPath $ f pageParams setPageParams f = setPath (const $ f pageParams)
setSearchQuery x = setPageParams $ _ { searchQuery = x } setSearchQuery x = setPageParams $ _ { searchQuery = x }
setTermListFilter x = setPageParams $ _ { termListFilter = x } setTermListFilter x = setPageParams $ _ { termListFilter = x }
setTermSizeFilter x = setPageParams $ _ { termSizeFilter = x } setTermSizeFilter x = setPageParams $ _ { termSizeFilter = x }
...@@ -196,21 +162,50 @@ toggleMap :: forall a. a -> Maybe a -> Maybe a ...@@ -196,21 +162,50 @@ toggleMap :: forall a. a -> Maybe a -> Maybe a
toggleMap _ (Just _) = Nothing toggleMap _ (Just _) = Nothing
toggleMap b Nothing = Just b toggleMap b Nothing = Just b
ngramsTableSpec :: CTabNgramType -> Spec State LoadedNgramsTableProps Action data Action'
ngramsTableSpec ntype = simpleSpec performAction render = SetParentResetChildren' (Maybe NgramsTerm)
| ToggleChild' (Maybe NgramsTerm) NgramsTerm
| Refresh'
performNgramsAction :: State -> Action' -> State
performNgramsAction st (SetParentResetChildren' term) = st
performNgramsAction st (ToggleChild' b c) = st
performNgramsAction st Refresh' = st
useNgramsReducer :: State -> R.Hooks (R.Reducer State Action')
useNgramsReducer init = R.useReducer performNgramsAction identity init
type Props =
( ends :: Ends
, tabNgramType :: CTabNgramType
, path :: R.State PageParams
, versioned :: VersionedNgramsTable )
ngramsTable :: Record Props -> R.Element
ngramsTable props = R.createElement ngramsTableCpt props []
ngramsTableCpt :: R.Component Props
ngramsTableCpt = R.hooksComponent "G.C.NgramsTable.ngramsTable" cpt
where
cpt {versioned} _ = do
state <- useNgramsReducer (initialState versioned)
pure $ R.fragment []
ngramsTableSpec :: Ends -> CTabNgramType -> R2.Setter PageParams -> Spec State (Record LoadedNgramsTableProps) Action
ngramsTableSpec ends ntype setPath = simpleSpec performAction render
where where
setParentResetChildren :: Maybe NgramsTerm -> State -> State setParentResetChildren :: Maybe NgramsTerm -> State -> State
setParentResetChildren p = _ { ngramsParent = p, ngramsChildren = mempty } setParentResetChildren p = _ { ngramsParent = p, ngramsChildren = mempty }
performAction :: PerformAction State LoadedNgramsTableProps Action performAction :: PerformAction State (Record LoadedNgramsTableProps) Action
performAction (SetParentResetChildren p) _ _ = performAction (SetParentResetChildren p) _ _ =
modifyState_ $ setParentResetChildren p modifyState_ $ setParentResetChildren p
performAction (ToggleChild b c) _ _ = performAction (ToggleChild b c) _ _ =
modifyState_ $ _ngramsChildren <<< at c %~ toggleMap b modifyState_ $ _ngramsChildren <<< at c %~ toggleMap b
performAction Refresh {path: {nodeId, listIds, tabType}} {ngramsVersion} = do performAction Refresh {path: {nodeId, listIds, tabType}} {ngramsVersion} = do
commitPatch {nodeId, listIds, tabType} (Versioned {version: ngramsVersion, data: mempty}) commitPatch ends {nodeId, listIds, tabType} (Versioned {version: ngramsVersion, data: mempty})
performAction (SetTermListItem n pl) {path: {nodeId, listIds, tabType}} {ngramsVersion} = performAction (SetTermListItem n pl) {path: {nodeId, listIds, tabType}} {ngramsVersion} =
commitPatch {nodeId, listIds, tabType} (Versioned {version: ngramsVersion, data: pt}) commitPatch ends {nodeId, listIds, tabType} (Versioned {version: ngramsVersion, data: pt})
where where
pe = NgramsPatch { patch_list: pl, patch_children: mempty } pe = NgramsPatch { patch_list: pl, patch_children: mempty }
pt = singletonNgramsTablePatch ntype n pe pt = singletonNgramsTablePatch ntype n pe
...@@ -223,42 +218,29 @@ ngramsTableSpec ntype = simpleSpec performAction render ...@@ -223,42 +218,29 @@ ngramsTableSpec ntype = simpleSpec performAction render
, ngramsVersion , ngramsVersion
} = do } = do
modifyState_ $ setParentResetChildren Nothing modifyState_ $ setParentResetChildren Nothing
commitPatch {nodeId, listIds, tabType} (Versioned {version: ngramsVersion, data: pt}) commitPatch ends {nodeId, listIds, tabType} (Versioned {version: ngramsVersion, data: pt})
where where
pc = patchSetFromMap ngramsChildren pc = patchSetFromMap ngramsChildren
pe = NgramsPatch { patch_list: mempty, patch_children: pc } pe = NgramsPatch { patch_list: mempty, patch_children: pc }
pt = singletonNgramsTablePatch ntype parent pe pt = singletonNgramsTablePatch ntype parent pe
performAction (AddNewNgram ngram) {path: {listIds, nodeId, tabType}} {ngramsVersion} = performAction (AddNewNgram ngram) {path: {listIds, nodeId, tabType}} {ngramsVersion} =
commitPatch {listIds, nodeId, tabType} (Versioned {version: ngramsVersion, data: pt}) commitPatch ends {listIds, nodeId, tabType} (Versioned {version: ngramsVersion, data: pt})
where where
pt = addNewNgram ntype ngram CandidateTerm pt = addNewNgram ntype ngram CandidateTerm
render :: Render State LoadedNgramsTableProps Action render :: Render State (Record LoadedNgramsTableProps) Action
render dispatch { path: pageParams render dispatch { path: pageParams
, loaded: Versioned { data: initTable } , loaded: Versioned { data: initTable } }
, dispatch: loaderDispatch }
{ ngramsTablePatch, ngramsParent, ngramsChildren } { ngramsTablePatch, ngramsParent, ngramsChildren }
_reactChildren = _reactChildren =
[ autoUpdateElt { duration: 3000 [ autoUpdateElt { duration: 3000, effect: dispatch Refresh }
, effect: dispatch Refresh , R2.scuff $ T.table { rows, setParams, container, colNames, totalRecords}
}
, T.tableElt
{ rows
, setParams
, container: tableContainer {pageParams, loaderDispatch, dispatch, ngramsParent, ngramsChildren, ngramsTable}
, colNames:
T.ColumnName <$>
[ "Map"
, "Stop"
, "Terms"
, "Score (Occurrences)" -- see convOrderBy
]
, totalRecords: 47361 -- TODO
}
] ]
where where
setParams params = totalRecords = 47361 -- TODO
loaderDispatch $ Loader.SetPath $ pageParams {params = params} colNames = T.ColumnName <$> ["Map", "Stop", "Terms", "Score (Occurrences)"] -- see convOrderBy
container = tableContainer {pageParams, setPath, dispatch, ngramsParent, ngramsChildren, ngramsTable}
setParams params = setPath $ const (pageParams {params = params})
ngramsTable = applyNgramsTablePatch ngramsTablePatch initTable ngramsTable = applyNgramsTablePatch ngramsTablePatch initTable
orderWith = orderWith =
case convOrderBy <$> pageParams.params.orderBy of case convOrderBy <$> pageParams.params.orderBy of
...@@ -287,29 +269,35 @@ ngramsTableSpec ntype = simpleSpec performAction render ...@@ -287,29 +269,35 @@ ngramsTableSpec ntype = simpleSpec performAction render
|| -- Unless they are scheduled to be removed. || -- Unless they are scheduled to be removed.
ngramsChildren ^. at ngrams == Just false ngramsChildren ^. at ngrams == Just false
convertRow (Tuple ngrams ngramsElement) = convertRow (Tuple ngrams ngramsElement) =
{ row: renderNgramsItem { ngramsTable, ngrams, ngramsParent, ngramsElement, dispatch} { row: R2.buff <$> renderNgramsItem { ngramsTable, ngrams, ngramsParent, ngramsElement, dispatch}
, delete: false , delete: false
} }
ngramsTableClass :: CTabNgramType -> Loader.InnerClass PageParams VersionedNgramsTable -- ngramsTableClass :: Ends -> CTabNgramType -> R2.Setter PageParams -> Loader.InnerClass PageParams (Versioned NgramsTable)
ngramsTableClass ct = createClass "NgramsTable" (ngramsTableSpec ct) initialState -- ngramsTableClass ends ct setPath = createClass "NgramsTable" (ngramsTableSpec ends ct setPath) initialState
-- ngramsTable' :: Ends -> CTabNgramType -> R2.Setter PageParams -> Record LoadedNgramsTableProps -> R.Element
-- ngramsTable' ends ct setPath props = R2.createElement' (ngramsTableClass ends ct setPath) props []
type MainNgramsTableProps = type MainNgramsTableProps =
{ nodeId :: Int ( nodeId :: Int
-- ^ This node can be a corpus or contact. -- ^ This node can be a corpus or contact.
, defaultListId :: Int , defaultListId :: Int
, tabType :: TabType , tabType :: TabType
} , ends :: Ends
, tabNgramType :: CTabNgramType
)
mainNgramsTableSpec :: CTabNgramType -> Spec {} MainNgramsTableProps Void mainNgramsTable :: Record MainNgramsTableProps -> R.Element
mainNgramsTableSpec nt = simpleSpec defaultPerformAction render mainNgramsTable props = R.createElement mainNgramsTableCpt props []
mainNgramsTableCpt :: R.Component MainNgramsTableProps
mainNgramsTableCpt = R.hooksComponent "MainNgramsTable" cpt
where where
render :: Render {} MainNgramsTableProps Void cpt {nodeId, defaultListId, tabType, ends, tabNgramType} _ = do
render _ {nodeId, defaultListId, tabType} _ _ = path <- R.useState' $ initialPageParams ends nodeId [defaultListId] tabType
[ ngramsLoader useLoader2 path (loadNgramsTable ends) $
{ path: initialPageParams nodeId [defaultListId] tabType \versioned -> ngramsTable {ends, tabNgramType, path, versioned}
, component: (ngramsTableClass nt)
} ]
type NgramsDepth = {ngrams :: NgramsTerm, depth :: Int} type NgramsDepth = {ngrams :: NgramsTerm, depth :: Int}
type NgramsClick = NgramsDepth -> Maybe (Effect Unit) type NgramsClick = NgramsDepth -> Maybe (Effect Unit)
...@@ -415,7 +403,6 @@ nextTermList GraphTerm = StopTerm ...@@ -415,7 +403,6 @@ nextTermList GraphTerm = StopTerm
nextTermList StopTerm = CandidateTerm nextTermList StopTerm = CandidateTerm
nextTermList CandidateTerm = GraphTerm nextTermList CandidateTerm = GraphTerm
optps1 :: forall a. Show a => { desc :: String, mval :: Maybe a } -> ReactElement optps1 :: forall a. Show a => { desc :: String, mval :: Maybe a } -> R.Element
optps1 { desc, mval } = option [value val] [text desc] optps1 { desc, mval } = H.option {value} [H.text desc]
where where value = maybe "" show mval
val = maybe "" show mval
...@@ -86,7 +86,7 @@ import Partial.Unsafe (unsafePartial) ...@@ -86,7 +86,7 @@ import Partial.Unsafe (unsafePartial)
import Gargantext.Utils.KarpRabin (indicesOfAny) import Gargantext.Utils.KarpRabin (indicesOfAny)
import Gargantext.Types (TermList(..), TermSize) import Gargantext.Types (TermList(..), TermSize)
import Gargantext.Config (toUrl, endConfigStateful, End(..), Path(..), TabType, OrderBy(..), CTabNgramType(..)) import Gargantext.Config (Ends, BackendRoute(..), TabType, OrderBy(..), CTabNgramType(..), url)
import Gargantext.Config.REST (get, put, post) import Gargantext.Config.REST (get, put, post)
import Gargantext.Components.Table as T import Gargantext.Components.Table as T
import Gargantext.Prelude import Gargantext.Prelude
...@@ -106,10 +106,11 @@ type PageParams = ...@@ -106,10 +106,11 @@ type PageParams =
, searchQuery :: String , searchQuery :: String
, termListFilter :: Maybe TermList -- Nothing means all , termListFilter :: Maybe TermList -- Nothing means all
, termSizeFilter :: Maybe TermSize -- Nothing means all , termSizeFilter :: Maybe TermSize -- Nothing means all
, ends :: Ends
) )
initialPageParams :: Int -> Array Int -> TabType -> PageParams initialPageParams :: Ends -> Int -> Array Int -> TabType -> PageParams
initialPageParams nodeId listIds tabType = initialPageParams ends nodeId listIds tabType =
{ nodeId { nodeId
, listIds , listIds
, params: T.initialParams , params: T.initialParams
...@@ -117,6 +118,7 @@ initialPageParams nodeId listIds tabType = ...@@ -117,6 +118,7 @@ initialPageParams nodeId listIds tabType =
, termSizeFilter: Nothing , termSizeFilter: Nothing
, termListFilter: Just GraphTerm , termListFilter: Just GraphTerm
, searchQuery: "" , searchQuery: ""
, ends
} }
type NgramsTerm = String type NgramsTerm = String
...@@ -561,47 +563,48 @@ type CoreState s = ...@@ -561,47 +563,48 @@ type CoreState s =
| s | s
} }
postNewNgrams :: forall s. Array NgramsTerm -> Maybe TermList -> CoreParams s -> Aff Unit postNewNgrams :: forall s. Ends -> Array NgramsTerm -> Maybe TermList -> CoreParams s -> Aff Unit
postNewNgrams newNgrams mayList {nodeId, listIds, tabType} = postNewNgrams ends newNgrams mayList {nodeId, listIds, tabType} =
when (not (A.null newNgrams)) $ do when (not (A.null newNgrams)) $ do
(_ :: Array Unit) <- post (toUrl endConfigStateful Back (PutNgrams tabType (head listIds) mayList) $ Just nodeId) newNgrams (_ :: Array Unit) <- post (url ends put) newNgrams
pure unit pure unit
where put = PutNgrams tabType (head listIds) mayList (Just nodeId)
postNewElems :: forall s. NewElems -> CoreParams s -> Aff Unit postNewElems :: forall s. Ends -> NewElems -> CoreParams s -> Aff Unit
postNewElems newElems params = void $ traverseWithIndex postNewElem newElems postNewElems ends newElems params = void $ traverseWithIndex postNewElem newElems
where where
postNewElem ngrams list = postNewNgrams [ngrams] (Just list) params postNewElem ngrams list = postNewNgrams ends [ngrams] (Just list) params
addNewNgram :: CTabNgramType -> NgramsTerm -> TermList -> NgramsTablePatch addNewNgram :: CTabNgramType -> NgramsTerm -> TermList -> NgramsTablePatch
addNewNgram ntype ngrams list = { ngramsPatches: mempty addNewNgram ntype ngrams list = { ngramsPatches: mempty
, ngramsNewElems: Map.singleton (normNgram ntype ngrams) list } , ngramsNewElems: Map.singleton (normNgram ntype ngrams) list }
putNgramsPatches :: {nodeId :: Int, listIds :: Array Int, tabType :: TabType} -> Versioned NgramsPatches -> Aff (Versioned NgramsPatches) putNgramsPatches :: Ends -> {nodeId :: Int, listIds :: Array Int, tabType :: TabType} -> Versioned NgramsPatches -> Aff (Versioned NgramsPatches)
putNgramsPatches {nodeId, listIds, tabType} = putNgramsPatches ends {nodeId, listIds, tabType} = put $ url ends putNgrams
put (toUrl endConfigStateful Back (PutNgrams tabType (head listIds) Nothing) $ Just nodeId) where putNgrams = PutNgrams tabType (head listIds) Nothing (Just nodeId)
commitPatch :: forall s. {nodeId :: Int, listIds :: Array Int, tabType :: TabType} commitPatch :: forall s. Ends -> {nodeId :: Int, listIds :: Array Int, tabType :: TabType}
-> Versioned NgramsTablePatch -> StateCoTransformer (CoreState s) Unit -> Versioned NgramsTablePatch -> StateCoTransformer (CoreState s) Unit
commitPatch props (Versioned {version, data: tablePatch@{ngramsPatches, ngramsNewElems}}) = do commitPatch ends props (Versioned {version, data: tablePatch@{ngramsPatches, ngramsNewElems}}) = do
let pt = Versioned { version, data: ngramsPatches } let pt = Versioned { version, data: ngramsPatches }
lift $ postNewElems ngramsNewElems props lift $ postNewElems ends ngramsNewElems props
Versioned {version: newVersion, data: newPatch} <- lift $ putNgramsPatches props pt Versioned {version: newVersion, data: newPatch} <- lift $ putNgramsPatches ends props pt
modifyState_ $ \s -> modifyState_ $ \s ->
s { ngramsVersion = newVersion s { ngramsVersion = newVersion
, ngramsTablePatch = fromNgramsPatches newPatch <> tablePatch <> s.ngramsTablePatch , ngramsTablePatch = fromNgramsPatches newPatch <> tablePatch <> s.ngramsTablePatch
} }
-- TODO: check that pt.version == s.ngramsTablePatch.version -- TODO: check that pt.version == s.ngramsTablePatch.version
loadNgramsTable :: PageParams -> Aff VersionedNgramsTable loadNgramsTable :: Ends -> PageParams -> Aff VersionedNgramsTable
loadNgramsTable { nodeId, listIds, termListFilter, termSizeFilter loadNgramsTable ends
, searchQuery, tabType, params: {offset, limit, orderBy}} = { nodeId, listIds, termListFilter, termSizeFilter
get $ toUrl endConfigStateful Back , searchQuery, tabType, params: {offset, limit, orderBy}}
(GetNgrams { tabType, offset, limit, listIds = get $ url ends query
where query = GetNgrams { tabType, offset, limit, listIds
, orderBy: convOrderBy <$> orderBy , orderBy: convOrderBy <$> orderBy
, termListFilter, termSizeFilter , termListFilter, termSizeFilter
, searchQuery , searchQuery } (Just nodeId)
})
(Just nodeId)
convOrderBy :: T.OrderByDirection T.ColumnName -> OrderBy convOrderBy :: T.OrderByDirection T.ColumnName -> OrderBy
convOrderBy (T.ASC (T.ColumnName "Score (Occurrences)")) = ScoreAsc convOrderBy (T.ASC (T.ColumnName "Score (Occurrences)")) = ScoreAsc
...@@ -609,10 +612,10 @@ convOrderBy (T.DESC (T.ColumnName "Score (Occurrences)")) = ScoreDesc ...@@ -609,10 +612,10 @@ convOrderBy (T.DESC (T.ColumnName "Score (Occurrences)")) = ScoreDesc
convOrderBy (T.ASC _) = TermAsc convOrderBy (T.ASC _) = TermAsc
convOrderBy (T.DESC _) = TermDesc convOrderBy (T.DESC _) = TermDesc
ngramsLoaderClass :: Loader.LoaderClass PageParams VersionedNgramsTable ngramsLoaderClass :: Ends -> Loader.LoaderClass PageParams VersionedNgramsTable
ngramsLoaderClass = Loader.createLoaderClass "NgramsTableLoader" loadNgramsTable ngramsLoaderClass ends = Loader.createLoaderClass "NgramsTableLoader" (loadNgramsTable ends)
ngramsLoader :: Loader.Props' PageParams VersionedNgramsTable -> ReactElement ngramsLoader :: Ends -> Loader.Props' PageParams VersionedNgramsTable -> ReactElement
ngramsLoader props = React.createElement ngramsLoaderClass props [] ngramsLoader ends props = React.createElement (ngramsLoaderClass ends) props []
type LoadedNgramsTableProps = Loader.InnerProps PageParams VersionedNgramsTable () type LoadedNgramsTableProps = ( path :: PageParams, loaded :: VersionedNgramsTable )
...@@ -167,8 +167,8 @@ instance encodeJsonCategoryQuery :: EncodeJson CategoryQuery where ...@@ -167,8 +167,8 @@ instance encodeJsonCategoryQuery :: EncodeJson CategoryQuery where
~> "ntc_category" := encodeJson post.category ~> "ntc_category" := encodeJson post.category
~> jsonEmptyObject ~> jsonEmptyObject
categoryUrl :: Int -> String categoryUrl :: Ends -> Int -> String
categoryUrl nodeId = toUrl endConfigStateful Back Node (Just nodeId) <> "/category" categoryUrl ends nodeId = url ends (NodeAPI Node (Just nodeId)) <> "/category"
putCategories :: Int -> CategoryQuery -> Aff (Array Int) putCategories :: Ends -> Int -> CategoryQuery -> Aff (Array Int)
putCategories nodeId = put $ categoryUrl nodeId putCategories = put <<< categoryUrl
...@@ -7,25 +7,25 @@ import Data.Maybe (Maybe(..), maybe) ...@@ -7,25 +7,25 @@ import Data.Maybe (Maybe(..), maybe)
import Data.Generic.Rep (class Generic) import Data.Generic.Rep (class Generic)
import Data.Generic.Rep.Show (genericShow) import Data.Generic.Rep.Show (genericShow)
import Data.Maybe (Maybe(..), maybe) import Data.Maybe (Maybe(..), maybe)
import Data.Tuple.Nested ((/\))
import Effect (Effect) import Effect (Effect)
import Effect.Class (liftEffect) import Effect.Class (liftEffect)
import Reactix as R
import Reactix.DOM.HTML as H
import Unsafe.Coerce (unsafeCoerce)
import Gargantext.Prelude
import Gargantext.Utils.Reactix as R2 import Gargantext.Utils.Reactix as R2
import React (ReactElement, ReactClass, Children, createElement)
import React.DOM (a, b, b', p, i, h3, hr, div, option, select, span, table, tbody, td, text, th, thead, tr)
import React.DOM.Props (className, href, onChange, onClick, scope, selected, value, defaultValue, style)
import Thermite (PerformAction, Render, Spec, modifyState_, simpleSpec, StateCoTransformer, createClass)
type TableContainerProps = type TableContainerProps =
{ pageSizeControl :: ReactElement ( pageSizeControl :: R.Element
, pageSizeDescription :: ReactElement , pageSizeDescription :: R.Element
, paginationLinks :: ReactElement , paginationLinks :: R.Element
, tableHead :: ReactElement , tableHead :: R.Element
, tableBody :: Array ReactElement , tableBody :: Array R.Element
} )
type Rows = Array { row :: Array ReactElement type Row = { row :: Array R.Element, delete :: Boolean }
, delete :: Boolean type Rows = Array Row
}
type OrderBy = Maybe (OrderByDirection ColumnName) type OrderBy = Maybe (OrderByDirection ColumnName)
...@@ -52,250 +52,203 @@ instance showOrderByDirection :: Show a => Show (OrderByDirection a) where ...@@ -52,250 +52,203 @@ instance showOrderByDirection :: Show a => Show (OrderByDirection a) where
derive instance eqOrderByDirection :: Eq a => Eq (OrderByDirection a) derive instance eqOrderByDirection :: Eq a => Eq (OrderByDirection a)
type Props' = type Props =
( colNames :: Array ColumnName ( colNames :: Array ColumnName
, totalRecords :: Int , totalRecords :: Int
, setParams :: Params -> Effect Unit , setParams :: Params -> Effect Unit
, rows :: Rows , rows :: Rows
, container :: TableContainerProps -> Array ReactElement , container :: Record TableContainerProps -> R.Element
) )
type Props = Record Props'
type State = type State =
{ currentPage :: Int { page :: Int
, pageSize :: PageSizes , pageSize :: PageSizes
, orderBy :: OrderBy , orderBy :: OrderBy
} }
initialState :: State stateParams :: State -> Params
initialState = stateParams {pageSize, page, orderBy} = {offset, limit, orderBy}
{ currentPage : 1 where
, pageSize : PS10 limit = pageSizes2Int pageSize
, orderBy : Nothing offset = limit * (page - 1)
}
initialParams :: Params
initialParams = stateParams initialState
data Action
= ChangePageSize PageSizes
| ChangePage Int
| ChangeOrderBy OrderBy
type ChangePageAction = Int -> Effect Unit
-- | Action
-- ChangePageSize
changePageSize :: PageSizes -> State -> State
changePageSize ps td =
td { pageSize = ps
, currentPage = 1
}
-- TODO: Not sure this is the right place for this function. type TableHeaderLayoutProps =
renderTableHeaderLayout :: { title :: String ( title :: String
, desc :: String , desc :: String
, query :: String , query :: String
, date :: String , date :: String
, user :: String , user :: String
} -> Array ReactElement )
renderTableHeaderLayout {title, desc, query, date, user} =
[ div [className "row"] initialParams :: Params
[ div [className "col-md-3"] [ h3 [] [text title] ] initialParams = stateParams {page: 1, pageSize: PS10, orderBy: Nothing}
, div [className "col-md-9"] [ hr [style {height : "2px",backgroundColor : "black"}] ] -- TODO: Not sure this is the right place for this
tableHeaderLayout :: Record TableHeaderLayoutProps -> R.Element
tableHeaderLayout props = R.createElement tableHeaderLayoutCpt props []
tableHeaderLayoutCpt :: R.Component TableHeaderLayoutProps
tableHeaderLayoutCpt = R.staticComponent "TableHeaderLayout" cpt
where
cpt {title, desc, query, date, user} _ =
R.fragment
[ H.div {className: "row"}
[ H.div {className: "col-md-3"} [ H.h3 {} [H.text title] ]
, H.div {className: "col-md-9"}
[ H.hr {style: {height: "2px", backgroundColor: "black"}} ]
] ]
, div [className "row"] [ div [className "jumbotron1", style {padding : "12px 0px 20px 12px"}] , H.div {className: "row"}
[ div [ className "col-md-8 content"] [ H.div {className: "jumbotron1", style: {padding: "12px 0px 20px 12px"}}
[ p [] [ i [className "glyphicon glyphicon-globe"] [] [ H.div {className: "col-md-8 content"}
, text $ " " <> desc [ H.p {}
[ H.i {className: "glyphicon glyphicon-globe"} []
, H.text $ " " <> desc
] ]
, p [] [ i [className "glyphicon glyphicon-zoom-in"] [] , H.p {}
, text $ " " <> query [ H.i {className: "glyphicon glyphicon-zoom-in"} []
, H.text $ " " <> query
] ]
] ]
, div [ className "col-md-4 content"] , H.div {className: "col-md-4 content"}
[ p [] [ i [className "glyphicon glyphicon-calendar"] [] [ H.p {}
, text $ " " <> date [ H.i {className: "glyphicon glyphicon-calendar"} []
, H.text $ " " <> date
] ]
, p [] [ i [className "glyphicon glyphicon-user"] [] , H.p {}
, text $ " " <> user [ H.i {className: "glyphicon glyphicon-user"} []
, H.text $ " " <> user
] ]
] ]
] ]
] ]
] ]
tableSpec :: Spec State Props Action table :: Record Props -> R.Element
tableSpec = simpleSpec performAction render table props = R.createElement tableCpt props []
tableCpt :: R.Component Props
tableCpt = R.hooksComponent "Table" cpt
where where
modifyStateAndReload :: (State -> State) -> Props -> State -> StateCoTransformer State Unit cpt {container, colNames, totalRecords, rows, setParams} _ = do
modifyStateAndReload f {setParams} state = do (pageSize /\ setPageSize) <- R.useState' PS10
--logs "modifyStateAndReload" -- TODO rename (page /\ setPage) <- R.useState' 1
modifyState_ f (orderBy /\ setOrderBy) <- R.useState' Nothing
liftEffect $ setParams $ stateParams $ f state let state = {pageSize, orderBy, page}
let ps = pageSizes2Int pageSize
performAction :: PerformAction State Props Action let totalPages = (totalRecords / ps) + min 1 (totalRecords `mod` ps)
performAction (ChangePageSize ps) = R.useEffect1' state $ setParams (stateParams state)
modifyStateAndReload $ changePageSize ps pure $ container
performAction (ChangePage p) = { pageSizeControl: sizeDD pageSize setPageSize
modifyStateAndReload $ _ { currentPage = p } , pageSizeDescription: textDescription page pageSize totalRecords
performAction (ChangeOrderBy mc) = , paginationLinks: pagination setPage totalPages page
modifyStateAndReload $ _ { orderBy = mc } , tableHead: H.tr {} (colHeader setOrderBy orderBy <$> colNames)
, tableBody: map (H.tr {} <<< map (\c -> H.td {} [c]) <<< _.row) rows
renderColHeader :: (OrderBy -> Effect Unit) }
-> OrderBy colHeader :: (R2.Setter OrderBy) -> OrderBy -> ColumnName -> R.Element
-> ColumnName -> ReactElement colHeader setOrderBy currentOrderBy c = H.th {scope: "col"} [ H.b {} cs ]
renderColHeader changeOrderBy currentOrderBy c =
th [scope "col"] [ b' cs ]
where where
lnk mc = effectLink (changeOrderBy mc) lnk mc = effectLink (setOrderBy (const mc))
cs :: Array ReactElement cs :: Array R.Element
cs = cs =
case currentOrderBy of case currentOrderBy of
Just (ASC d) | c == d -> [lnk (Just (DESC c)) "DESC ", lnk Nothing (columnName c)] Just (ASC d) | c == d -> [lnk (Just (DESC c)) "DESC ", lnk Nothing (columnName c)]
Just (DESC d) | c == d -> [lnk (Just (ASC c)) "ASC ", lnk Nothing (columnName c)] Just (DESC d) | c == d -> [lnk (Just (ASC c)) "ASC ", lnk Nothing (columnName c)]
_ -> [lnk (Just (ASC c)) (columnName c)] _ -> [lnk (Just (ASC c)) (columnName c)]
render :: Render State Props Action defaultContainer :: {title :: String} -> Record TableContainerProps -> R.Element
render dispatch {container, colNames, totalRecords, rows} defaultContainer {title} props = R.fragment
{pageSize, currentPage, orderBy} _ = [ H.div {className: "row"}
container [ H.div {className: "col-md-4"} [ props.pageSizeDescription ]
{ pageSizeControl: sizeDD pageSize dispatch , H.div {className: "col-md-4"} [ props.paginationLinks ]
, pageSizeDescription: textDescription currentPage pageSize totalRecords , H.div {className: "col-md-4"} [ props.pageSizeControl ]
, paginationLinks: pagination (dispatch <<< ChangePage) totalPages currentPage
, tableHead:
tr [] (renderColHeader (dispatch <<< ChangeOrderBy) orderBy <$> colNames)
, tableBody:
map (tr [] <<< map (\c -> td [] [c]) <<< _.row) rows
}
where
ps = pageSizes2Int pageSize
totalPages = (totalRecords / ps) + min 1 (totalRecords `mod` ps)
defaultContainer :: {title :: String} -> TableContainerProps -> Array ReactElement
defaultContainer {title} props =
[ div [className "row"]
[ div [className "col-md-4"] [props.pageSizeDescription]
, div [className "col-md-4"] [props.paginationLinks]
, div [className "col-md-4"] [props.pageSizeControl]
] ]
, table [ className "table"] , H.table {className: "table"}
[ thead [className "thead-dark"] [ props.tableHead ] [ H.thead {className: "thead-dark"} [ props.tableHead ]
, tbody [] props.tableBody , H.tbody {} props.tableBody
] ]
] ]
-- TODO: this needs to be in Gargantext.Pages.Corpus.Graph.Tabs -- TODO: this needs to be in Gargantext.Pages.Corpus.Graph.Tabs
graphContainer :: {title :: String} -> TableContainerProps -> Array ReactElement graphContainer :: {title :: String} -> Record TableContainerProps -> R.Element
graphContainer {title} props = graphContainer {title} props =
[ -- TODO title in tabs name (above) -- TODO title in tabs name (above)
table [ className "table"] H.table {className: "table"}
[ thead [className "thead-dark"] [ props.tableHead ] [ H.thead {className: "thead-dark"} [ props.tableHead ]
, tbody [] props.tableBody , H.tbody {} props.tableBody
] ]
-- TODO better rendering of the paginationLinks -- TODO better rendering of the paginationLinks
-- , props.pageSizeControl -- , props.pageSizeControl
-- , props.pageSizeDescription -- , props.pageSizeDescription
-- , props.paginationLinks -- , props.paginationLinks
]
sizeDD :: PageSizes -> R2.Setter PageSizes -> R.Element
stateParams :: State -> Params sizeDD ps setPageSize = H.span {} [ R2.select { className, on: {change} } sizes ]
stateParams {pageSize, currentPage, orderBy} = {offset, limit, orderBy}
where where
limit = pageSizes2Int pageSize className = "form-control"
offset = limit * (currentPage - 1) change e = setPageSize $ const (string2PageSize $ R2.unsafeEventValue e)
sizes = map (optps ps) pageSizes
paramsState :: Params -> State
paramsState {offset, limit, orderBy} = {pageSize, currentPage, orderBy}
where
pageSize = string2PageSize $ show limit
currentPage = (offset / limit) + 1
tableClass :: ReactClass {children :: Children | Props'}
tableClass = createClass "Table" tableSpec (const initialState)
tableElt :: Props -> ReactElement
tableElt props = createElement tableClass props []
tableEltWithInitialState :: State -> Props -> ReactElement textDescription :: Int -> PageSizes -> Int -> R.Element
tableEltWithInitialState state props = createElement tc props [] textDescription currPage pageSize totalRecords =
where H.div {className: "row1"} [ H.div {className: ""} [ H.text msg ] ] -- TODO or col-md-6 ?
tc = createClass "Table" tableSpec (const state)
sizeDD :: PageSizes -> (Action -> Effect Unit) -> ReactElement
sizeDD ps d
= span []
[ select [ className "form-control"
, defaultValue $ show ps
, onChange $ \e -> d (ChangePageSize $ string2PageSize $ R2.unsafeEventValue e)
] $ map (optps ps) aryPS
]
textDescription :: Int -> PageSizes -> Int -> ReactElement
textDescription currPage pageSize totalRecords
= div [className "row1"]
[ div [className ""] -- TODO or col-md-6 ?
[ text $ "Showing " <> show start <> " to " <> show end <> " of " <> show totalRecords ]
]
where where
start = (currPage - 1) * pageSizes2Int pageSize + 1 start = (currPage - 1) * pageSizes2Int pageSize + 1
end' = currPage * pageSizes2Int pageSize end' = currPage * pageSizes2Int pageSize
end = if end' > totalRecords then totalRecords else end' end = if end' > totalRecords then totalRecords else end'
msg = "Showing " <> show start <> " to " <> show end <> " of " <> show totalRecords
effectLink :: Effect Unit -> String -> ReactElement effectLink :: Effect Unit -> String -> R.Element
effectLink eff msg = a [onClick $ const eff] [text msg] effectLink eff msg = H.a {on: {click: const eff}} [H.text msg]
pagination :: ChangePageAction -> Int -> Int -> ReactElement pagination :: (R2.Setter Int) -> Int -> Int -> R.Element
pagination changePage tp cp pagination changePage tp cp =
= span [] $ H.span {} $
[ text " ", prev, first, ldots] [ H.text " ", prev, first, ldots]
<> <>
lnums lnums
<> <>
[b' [text $ " " <> show cp <> " "]] [H.b {} [H.text $ " " <> show cp <> " "]]
<> <>
rnums rnums
<> <>
[ rdots, last, next ] [ rdots, last, next ]
where where
prev = if cp == 1 then prev = if cp == 1 then
text " Prev. " H.text " Prev. "
else else
changePageLink (cp - 1) "Prev." changePageLink (cp - 1) "Prev."
next = if cp == tp then next = if cp == tp then
text " Next " H.text " Next "
else else
changePageLink (cp + 1) "Next" changePageLink (cp + 1) "Next"
first = if cp == 1 then first = if cp == 1 then
text "" H.text ""
else else
changePageLink' 1 changePageLink' 1
last = if cp == tp then last = if cp == tp then
text "" H.text ""
else else
changePageLink' tp changePageLink' tp
ldots = if cp >= 5 then ldots = if cp >= 5 then
text " ... " H.text " ... "
else else
text "" H.text ""
rdots = if cp + 3 < tp then rdots = if cp + 3 < tp then
text " ... " H.text " ... "
else else
text "" H.text ""
lnums = map changePageLink' $ filter (1 < _) [cp - 2, cp - 1] lnums = map changePageLink' $ filter (1 < _) [cp - 2, cp - 1]
rnums = map changePageLink' $ filter (tp > _) [cp + 1, cp + 2] rnums = map changePageLink' $ filter (tp > _) [cp + 1, cp + 2]
changePageLink :: Int -> String -> ReactElement changePageLink :: Int -> String -> R.Element
changePageLink i s = span [] changePageLink i s =
[ text " " H.span {}
, effectLink (changePage i) s [ H.text " "
, text " " , effectLink (changePage (const i)) s
, H.text " "
] ]
changePageLink' :: Int -> ReactElement changePageLink' :: Int -> R.Element
changePageLink' i = changePageLink i (show i) changePageLink' i = changePageLink i (show i)
data PageSizes = PS10 | PS20 | PS50 | PS100 | PS200 data PageSizes = PS10 | PS20 | PS50 | PS100 | PS200
...@@ -316,8 +269,8 @@ pageSizes2Int PS50 = 50 ...@@ -316,8 +269,8 @@ pageSizes2Int PS50 = 50
pageSizes2Int PS100 = 100 pageSizes2Int PS100 = 100
pageSizes2Int PS200 = 200 pageSizes2Int PS200 = 200
aryPS :: Array PageSizes pageSizes :: Array PageSizes
aryPS = [PS10, PS20, PS50, PS100, PS200] pageSizes = [PS10, PS20, PS50, PS100, PS200]
string2PageSize :: String -> PageSizes string2PageSize :: String -> PageSizes
string2PageSize "10" = PS10 string2PageSize "10" = PS10
...@@ -327,5 +280,5 @@ string2PageSize "100" = PS100 ...@@ -327,5 +280,5 @@ string2PageSize "100" = PS100
string2PageSize "200" = PS200 string2PageSize "200" = PS200
string2PageSize _ = PS10 string2PageSize _ = PS10
optps :: PageSizes -> PageSizes -> ReactElement optps :: PageSizes -> PageSizes -> R.Element
optps cv val = option [ value $ show val ] [text $ show val] optps cv val = H.option {selected: (cv == val), value: show val} [R2.showText val]
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment