Dashboard.purs 15.7 KB
Newer Older
1
module Gargantext.Components.Nodes.Corpus.Dashboard where
2

3 4 5 6
import Gargantext.Components.Nodes.Types
import Gargantext.Prelude

import DOM.Simple.Console (log, log2)
7
import Data.Array as A
8
import Data.List as List
9
import Data.Maybe (Maybe(..), fromMaybe)
10
import Data.Tuple (Tuple(..), snd)
11 12
import Data.Tuple.Nested ((/\))
import Effect (Effect)
13 14
import Effect.Aff (launchAff_)
import Effect.Class (liftEffect)
15
import Gargantext.Components.Nodes.Corpus (fieldsCodeEditor)
16
import Gargantext.Components.Nodes.Corpus.Chart.Predefined as P
17
import Gargantext.Components.Nodes.Dashboard.Types as DT
18
import Gargantext.Hooks.Loader (useLoader)
19
import Gargantext.Sessions (Session, sessionId)
20
import Gargantext.Types (NodeID)
21
import Gargantext.Utils.Reactix as R2
22
import Gargantext.Utils.Reload as GUR
23 24
import Reactix as R
import Reactix.DOM.HTML as H
25

26
thisModule :: String
27 28
thisModule = "Gargantext.Components.Nodes.Corpus.Dashboard"

29
type Props =
30
  ( nodeId :: NodeID
31 32
  , session :: Session
  )
33

34 35 36
dashboardLayout :: R2.Component Props
dashboardLayout = R.createElement dashboardLayoutCpt

37 38 39
dashboardLayoutCpt :: R.Component Props
dashboardLayoutCpt = R.hooksComponentWithModule thisModule "dashboardLayout" cpt
  where
40 41 42
    cpt { nodeId, session } _ = do
      let sid = sessionId session

43
      pure $ dashboardLayoutWithKey { key: show sid <> "-" <> show nodeId, nodeId, session } []
44 45 46 47 48 49

type KeyProps = (
  key :: String
  | Props
  )

50 51 52
dashboardLayoutWithKey :: R2.Component KeyProps
dashboardLayoutWithKey = R.createElement dashboardLayoutWithKeyCpt

53 54 55
dashboardLayoutWithKeyCpt :: R.Component KeyProps
dashboardLayoutWithKeyCpt = R.hooksComponentWithModule thisModule "dashboardLayoutWithKey" cpt
  where
56
    cpt { nodeId, session } _ = do
57
      reload <- GUR.new
58

59
      useLoader {nodeId, reload: GUR.value reload, session} DT.loadDashboardWithReload $
60
        \dashboardData@{hyperdata: DT.Hyperdata h, parentId} -> do
61
          let { charts, fields } = h
62
          dashboardLayoutLoaded { charts
63 64
                                , corpusId: parentId
                                , defaultListId: 0
65
                                , fields
66
                                , nodeId
67
                                , onChange: onChange nodeId reload (DT.Hyperdata h)
68
                                , session } []
69
      where
70 71 72
        onChange :: NodeID -> GUR.ReloadS -> DT.Hyperdata -> { charts :: Array P.PredefinedChart
                                                         , fields :: List.List FTField } -> Effect Unit
        onChange nodeId' reload (DT.Hyperdata h) { charts, fields } = do
73
          launchAff_ do
74
            DT.saveDashboard { hyperdata: DT.Hyperdata $ h { charts = charts, fields = fields }
75 76
                             , nodeId:nodeId'
                             , session }
77
            liftEffect $ GUR.bump reload
78 79

type LoadedProps =
80
  ( charts :: Array P.PredefinedChart
81
  , corpusId :: NodeID
82
  , defaultListId :: Int
83
  , fields :: List.List FTField
84 85
  , onChange :: { charts :: Array P.PredefinedChart
               , fields :: List.List FTField } -> Effect Unit
86 87 88
  | Props
  )

89 90 91
dashboardLayoutLoaded :: R2.Component LoadedProps
dashboardLayoutLoaded = R.createElement dashboardLayoutLoadedCpt

92 93 94
dashboardLayoutLoadedCpt :: R.Component LoadedProps
dashboardLayoutLoadedCpt = R.hooksComponentWithModule thisModule "dashboardLayoutLoaded" cpt
  where
95
    cpt props@{ charts, corpusId, defaultListId, fields, nodeId, onChange, session } _ = do
96
      pure $ H.div {}
97 98 99 100 101
        [ dashboardCodeEditor { fields
                              , nodeId
                              , onChange: \fs -> onChange { charts, fields: fs }
                              , session } []
        ,  H.div { className: "row" }
102 103 104 105 106 107 108
          [ H.div { className: "col-12" }
            ([ H.h1 {} [ H.text "Board" ]
             , H.p {}  [ H.text "Summary of all your charts here" ]
             ] <> chartsEls <> [addNew])
          ]
        ]
      where
109

110
        addNew = H.div { className: "row" } [
111
          H.span { className: "btn btn-primary"
112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133
                 , on: { click: onClickAddChart }} [ H.span { className: "fa fa-plus" } [] ]
          ]
          where
            onClickAddChart _ = onChange { charts: A.cons P.CDocsHistogram charts
                                         , fields }
        chartsEls = A.mapWithIndex chartIdx charts
        chartIdx idx chart =
          renderChart { chart, corpusId, defaultListId, onChange: onChangeChart, onRemove, session } []
          where
            onChangeChart c = do
              onChange { charts: fromMaybe charts (A.modifyAt idx (\_ -> c) charts)
                       , fields }
            onRemove _ = onChange { charts: fromMaybe charts $ A.deleteAt idx charts
                                  , fields }

type CodeEditorProps =
  ( fields :: List.List FTField
  , onChange :: List.List FTField -> Effect Unit
  | Props
  )

dashboardCodeEditor :: R2.Component CodeEditorProps
134
dashboardCodeEditor = R.createElement dashboardCodeEditorCpt
135

136 137 138
dashboardCodeEditorCpt :: R.Component CodeEditorProps
dashboardCodeEditorCpt = R.hooksComponentWithModule thisModule "dashboardCodeEditor" cpt
  where
139
    cpt props@{ fields, nodeId, onChange, session } _ = do
140 141 142 143 144 145 146 147 148 149 150 151
      let fieldsWithIndex = List.mapWithIndex (\idx -> \t -> Tuple idx t) fields
      fieldsS <- R.useState' fieldsWithIndex
      fieldsRef <- R.useRef fields

      -- handle props change of fields
      R.useEffect1' fields $ do
        if R.readRef fieldsRef == fields then
          pure unit
        else do
          R.setRef fieldsRef fields
          snd fieldsS $ const fieldsWithIndex

152
      pure $ R.fragment
153
        [ H.div { className: "row" }
154
          [ H.div { className: "btn btn-primary " <> (saveEnabled fieldsWithIndex fieldsS)
155 156 157 158
                  , on: { click: onClickSave fieldsS }
                  }
            [ H.span { className: "fa fa-floppy-o" } [  ]
            ]
159 160 161 162 163
          ]
        , H.div { className: "row" }
          [ H.div { className: "col-12" }
            [ fieldsCodeEditor { fields: fieldsS
                               , nodeId
164 165
                               , session} []
            ]
166 167
          ]
        , H.div { className: "row" }
168
          [ H.div { className: "btn btn-primary"
169 170 171 172 173 174
                  , on: { click: onClickAddField fieldsS }
                  }
            [ H.span { className: "fa fa-plus" } [  ]
            ]
          ]
        ]
175
      where
176 177 178 179 180 181 182 183 184 185 186 187
        saveEnabled :: FTFieldsWithIndex -> R.State FTFieldsWithIndex -> String
        saveEnabled fs (fsS /\ _) = if fs == fsS then "disabled" else "enabled"

        onClickSave :: forall e. R.State FTFieldsWithIndex -> e -> Effect Unit
        onClickSave (fields /\ _) _ = do
          log "[dashboardCodeEditor] saving (TODO)"
          onChange $ snd <$> fields
          -- launchAff_ do
            -- saveCorpus $ { hyperdata: Hyperdata {fields: (\(Tuple _ f) -> f) <$> fieldsS}
            --             , nodeId
            --             , session }

188 189 190
        onClickAddField :: forall e. R.State FTFieldsWithIndex -> e -> Effect Unit
        onClickAddField (_ /\ setFieldsS) _ = do
          setFieldsS $ \fieldsS -> List.snoc fieldsS $ Tuple (List.length fieldsS) defaultField
191 192

type PredefinedChartProps =
193
  ( chart :: P.PredefinedChart
194
  , corpusId :: NodeID
195 196 197 198 199 200
  , defaultListId :: Int
  , onChange :: P.PredefinedChart -> Effect Unit
  , onRemove :: Unit -> Effect Unit
  , session :: Session
  )

201 202 203
renderChart :: R2.Component PredefinedChartProps
renderChart = R.createElement renderChartCpt

204 205 206
renderChartCpt :: R.Component PredefinedChartProps
renderChartCpt = R.hooksComponentWithModule thisModule "renderChart" cpt
  where
207
    cpt { chart, corpusId, defaultListId, onChange, onRemove, session } _ = do
208 209 210 211 212 213 214 215
      pure $ H.div { className: "row chart card" }
        [ H.div { className: "card-header" }
          [ H.div { className: "row" }
            [ H.div { className: "col-2" }
              [ R2.select { defaultValue: show chart
                          , on: { change: onSelectChange }
                          } (option <$> P.allPredefinedCharts)
              ]
216
            , H.div { className: "col-9" } []
217 218 219 220
            , H.div { className: "col-1" }
              [ H.span { className: "btn btn-danger"
                       , on: { click: onRemoveClick }} [ H.span { className: "fa fa-trash" } [] ]
              ]
221
            ]
222
          ]
223 224 225 226 227
        , H.div { className: "card-body" }
          [ H.div { className: "row" }
            [ H.div { className: "col-12 chart" }
              [ P.render chart params ]
            ]
228 229
          ]
        ]
230
      where
231 232
        option pc =
          H.option { value: show pc } [ H.text $ show pc ]
233
        onSelectChange e = onChange $ fromMaybe P.CDocsHistogram $ read value
234
          where
235
            value = R.unsafeEventValue e
236 237 238 239 240 241
        onRemoveClick _ = onRemove unit
        params = { corpusId
                 , limit: Just 1000
                 , listId: Just defaultListId
                 , session
                 }
242

243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259
    -- aSchool school = H.div {className: "col-md-4 content"} [ chart $ focus school ]
    -- schools = [ "Télécom Bretagne", "Mines Nantes", "Eurecom" ]
    -- myData =
    --   [seriesBarD1 {name: "Bar Data"}
    --    [ dataSerie {name: "val1", value: 50.0}
    --    , dataSerie {name: "val2", value: 70.0}
    --    , dataSerie {name: "val3", value: 80.0} ] ]
    -- focus :: String -> Options
    -- focus school =
    --   Options
    --   { mainTitle : "Focus " <> school
    --   , subTitle  : "Total scientific publications"
    --   , xAxis     : xAxis' ["2015", "2016", "2017"]
    --   , yAxis     : yAxis' { position: "left", show: false, min : 0 }
    --   , series    : myData
    --   , addZoom   : false
    --   , tooltip   : tooltipTriggerAxis } -- Necessary?
260

261 262
-----------------------------------------------------------------------------------------------------------

263 264
-- naturePublis_x :: Array String
-- naturePublis_x = ["Com","Articles","Thèses","Reports"]
265

266 267
-- naturePublis_y' :: Array Int
-- naturePublis_y' = [23901,17417,1188,1176]
268

269 270
-- naturePublis_y :: Array DataD1
-- naturePublis_y = zipWith (\n v -> dataSerie {name: n, value: toNumber v }) naturePublis_x naturePublis_y'
271

272 273 274 275 276 277 278 279 280 281
-- naturePublis :: Options
-- naturePublis = Options
--   { mainTitle : "Nature of publications"
--   , subTitle  : "Distribution by type"
--   , xAxis     : xAxis' []
--   , yAxis     : yAxis' { position: "left", show: false, min:0}
--   , series    : [seriesFunnelD1 { name: "Funnel Data" } naturePublis_y]
--   , addZoom   : false
--   , tooltip   : tooltipTriggerAxis -- Necessary?
--   }
282 283 284

-----------------------------------------------------------------------------------------------------------

285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317
-- globalPublis_x :: Array Int
-- globalPublis_x = [1982,1986,1987,1988,1990,1993,1996,1997,1998,1999,2000,2001,2002,2003,2004,2005,2006,2007,2008,2009,2010,2011,2012,2013,2014,2015,2016,2017]
-- globalPublis_y :: Array Int
-- globalPublis_y = [1,4,2,1,1,2,1,1,8,38,234,76,40,82,75,202,1475,1092,1827,2630,4978,3668,4764,5915,4602,5269,6814,4018]


-- globalPublis :: Options
-- globalPublis = Options
--   { mainTitle : "Histogram"
--   , subTitle  : "Distribution of publications over time"
--   , xAxis     : xAxis' (map show globalPublis_x)
--   , yAxis     : yAxis' { position: "left", show: true, min:0}
--   , series    : [seriesBarD1 {name: "Number of publication / year"} $ map (\n -> dataSerie {name: "", value: toNumber n }) globalPublis_y]
--   , addZoom   : true
--   , tooltip   : tooltipTriggerAxis -- Necessary?
--   }



-- distriBySchool_y :: Array (Tuple String Int)
-- distriBySchool_y = [Tuple "Télécom Bretagne" 1150,Tuple "Télécom SudParis" 946,Tuple "Mines Nantes" 547,Tuple "Télécom ParisTech" 429,Tuple "IMT Atlantique" 205,Tuple "Mines Alès" 56
--                    ,Tuple "Télécom Ecole de Management" 52,Tuple "Mines Albi-Carmaux" 6]

-- distriBySchool :: Options
-- distriBySchool = Options
--   { mainTitle : "School production in 2017"
--   , subTitle  : "Distribution by school"
--   , xAxis     : xAxis' []
--   , yAxis     : yAxis' { position : "", show: false, min:0}
--   , series    : [ seriesPieD1 {name: "Pie data"} (map (\(Tuple n v) -> dataSerie {name: n, value: toNumber v}) distriBySchool_y)]
--   , addZoom   : false
--   , tooltip   : tooltipTriggerAxis -- Necessary?
--   }
318

319 320 321 322 323 324 325 326 327 328 329 330 331
-- scatterEx :: Options
-- scatterEx = Options
--   { mainTitle : "Scatter test"
--   , subTitle  : "Scatter subtitle"
--   , xAxis     : xAxis' []
--   , yAxis     : yAxis' { position: "", show: true, min:0}
--   , series    : [ seriesScatterD2 {name: "name1", symbolSize: 10.0} (dataSerieV <$> [[2.0,3.0],[3.0,4.0]])
--                 , seriesScatterD2 {name: "name2", symbolSize: 5.0 } (dataSerieV <$> [[1.0,3.0],[5.0,4.0]])
--                 , seriesScatterD2 {name: "name3", symbolSize: 10.0} (dataSerieV <$> [[10.0,3.0],[8.0,4.0]])
--                 ]
--   , addZoom   : false
--   , tooltip   : tooltipTriggerAxis -- Necessary?
--   }
332

333 334 335 336 337 338 339 340 341 342 343 344 345 346 347 348 349 350 351 352 353 354 355
-- sankeyEx :: Options
-- sankeyEx = Options
--   { mainTitle : ""
--   , subTitle  : ""
--   , xAxis     : xAxis' []
--   , yAxis     : yAxis' { position: "", show: false, min:0}
--   , series    :
--      [ seriesSankey
--          { "data":
--              [ {name : "a"}, {name : "b"}
--              , {name:"c"},   {name:"d"} ]
--          , links:
--              [ {source : "a", target : "b", value :2.0}
--              , {source : "a", target : "c", value :1.0}
--              , {source : "b", target : "c", value :1.0}
--              , {source : "b", target : "d", value :3.0}
--              ]
--          , layout: "none"
--          }
--      ]
--   , tooltip   : tooltipTriggerAxis -- Necessary?
--   , addZoom   : false
--   }
356

357 358 359 360 361 362 363 364 365 366 367 368 369 370 371 372 373 374 375 376 377 378 379 380 381 382 383
-- treeData :: Array TreeNode
-- treeData =
--   [ treeNode "nodeA" 10
--     [ treeNode "nodeAa" 4 []
--     , treeNode "nodeAb" 5 []
--     , treeNode "nodeAc" 1
--       [ treeNode "nodeAca" 5 []
--       , treeNode "nodeAcb" 5 [] ] ]
--   , treeNode "nodeB" 20
--     [ treeNode "nodeBa" 20
--       [ treeNode "nodeBa1" 20 [] ]]
--   , treeNode "nodeC" 20
--     [ treeNode "nodeCa" 20
--       [ treeNode "nodeCa1" 10 []
--       , treeNode "nodeCa2" 10 [] ]
--     , treeNode "nodeD" 20
--       [ treeNode "nodeDa" 20
--         [ treeNode "nodeDa1" 2 []
--         , treeNode "nodeDa2" 2 []
--         , treeNode "nodeDa3" 2 []
--         , treeNode "nodeDa4" 2 []
--         , treeNode "nodeDa5" 2 []
--         , treeNode "nodeDa6" 2 []
--         , treeNode "nodeDa7" 2 []
--         , treeNode "nodeDa8" 2 []
--         , treeNode "nodeDa9" 2 []
--         , treeNode "nodeDa10" 2 [] ]]]]
384

385 386 387 388 389 390 391 392 393 394 395 396 397
-- treeData' :: Array TreeNode
-- treeData' =
--   [ treeNode "nodeA" 10
--     [ treeLeaf "nodeAa" 4
--     , treeLeaf "nodeAb" 5
--     , treeNode "nodeAc" 1 [ treeLeaf "nodeAca" 5, treeLeaf "nodeAcb" 5 ]]
--   , treeNode "nodeB" 20 [ treeNode "nodeBa" 20 [ treeLeaf "nodeBa1" 20]]
--   , treeNode "nodeC" 20 [ treeNode "nodeBa" 20 [ treeLeaf "nodeBa1" 20]]
--   , treeNode "nodeD" 20 [ treeNode "nodeBa" 20 [ treeLeaf "nodeBa1" 20]]
--   , treeNode "nodeE" 20 [ treeNode "nodeBa" 20 [ treeLeaf "nodeBa1" 20]]
--   , treeNode "nodeF" 20 [ treeNode "nodeBa" 20 [ treeLeaf "nodeBa1" 20]]
--   , treeNode "nodeG" 20 [ treeNode "nodeBa" 20 [ treeLeaf "nodeBa1" 20]]
--   , treeNode "nodeH" 20 [ treeNode "nodeBa" 20 [ treeLeaf "nodeBa1" 20]]]
398

399 400 401 402 403 404 405 406 407 408
-- treeMapEx :: Options
-- treeMapEx = Options
--   { mainTitle : ""
--   , subTitle  : ""
--   , xAxis     : xAxis' []
--   , yAxis     : yAxis' { position: "", show: false, min:0}
--   , series    : [mkTree TreeMap treeData]
--   , addZoom   : false
--   , tooltip   : tooltipTriggerAxis -- Necessary?
--   }
409

410 411 412 413 414 415 416 417 418 419
-- treeEx :: Options
-- treeEx = Options
--   { mainTitle : "Tree"
--   , subTitle  : "Radial"
--   , xAxis     : xAxis' []
--   , yAxis     : yAxis' { position: "", show: false, min:0}
--   , series    : [mkTree TreeRadial treeData']
--   , addZoom   : false
--   , tooltip   : tooltipTriggerAxis -- Necessary?
--   }