Browse Source

integration of html-document

Skylsmoi 5 years ago
parent
commit
842dc94210

+ 8 - 8
dist/index.html View File

@@ -26,7 +26,7 @@
26 26
     <script src="https://maxcdn.bootstrapcdn.com/bootstrap/4.0.0-beta.2/js/bootstrap.min.js" integrity="sha384-alpBpkh1PFOepccYVYDB4do5UnbKysX5WZXm3XxPqe5iKTfUKjNkCk9SaVuEZflJ" crossorigin="anonymous"></script>
27 27
     -->
28 28
 
29
-    <script src='/app/pageHtml.app.js'></script>
29
+    <script src='/app/html-documents.app.js'></script>
30 30
     <script src='/app/thread.app.js'></script>
31 31
     <script src='/app/file.app.js'></script>
32 32
 
@@ -42,11 +42,11 @@
42 42
 
43 43
       getSelectedApp = name => {
44 44
         switch (name) {
45
-          case 'PageHtml':
46
-            return appPageHtml
47
-          case 'Thread':
45
+          case 'html-documents':
46
+            return appHtmlDocument
47
+          case 'thread':
48 48
             return appThread
49
-          case 'File':
49
+          case 'file':
50 50
             return appFile
51 51
         }
52 52
       }
@@ -54,10 +54,10 @@
54 54
       GLOBAL_renderApp = app => {
55 55
         console.log('GLOBAL_renderApp', app)
56 56
 
57
-        const selectedApp = getSelectedApp(app.config.name)
57
+        const selectedApp = getSelectedApp(app.config.slug)
58 58
 
59 59
         if (selectedApp.isRendered) {
60
-          GLOBAL_dispatchEvent({type: `${app.config.name}_showApp`, data: app})
60
+          GLOBAL_dispatchEvent({type: `${app.config.slug}_showApp`, data: app})
61 61
         } else {
62 62
           selectedApp.renderApp(app)
63 63
           selectedApp.isRendered = true
@@ -69,7 +69,7 @@
69 69
       GLOBAL_renderCreateContentApp = app => {
70 70
         console.log('GLOBAL_renderCreateContentApp', app)
71 71
 
72
-        getSelectedApp(app.config.name).renderPopupCreation(app)
72
+        getSelectedApp(app.config.slug).renderPopupCreation(app)
73 73
       }
74 74
 
75 75
       GLOBAL_dispatchEvent = ({ type, data }) => {

+ 9 - 0
jsonserver/server.js View File

@@ -99,6 +99,10 @@ server.get('/workspace/:idws/contents/:idc', (req, res) => {
99 99
   }
100 100
 })
101 101
 
102
+server.put('/workspaces/:idws/html-documents/:idcts', (req, res) => {
103
+  return res.jsonp(jsonDb.putHtmlDoc)
104
+})
105
+
102 106
 server.get('/workspace/:idws/contents/:idc/timeline', (req, res) => {
103 107
   switch (req.params.idc) {
104 108
     case '1': // pageHtml
@@ -111,6 +115,11 @@ server.get('/workspace/:idws/contents/:idc/timeline', (req, res) => {
111 115
   }
112 116
 })
113 117
 
118
+server.post('/workspaces/:idws/contents', (req, res) => {
119
+  console.log(req.body)
120
+  return res.jsonp('gg')
121
+})
122
+
114 123
 server.use(router)
115 124
 server.listen(GLOBAL_PORT, () => {
116 125
   console.log('JSON Server is running on port : ' + GLOBAL_PORT)

+ 30 - 1
jsonserver/static_db.json View File

@@ -419,5 +419,34 @@
419 419
     },
420 420
     "role": "content_manager",
421 421
     "subscribed_to_notif": true
422
-  }]
422
+  }],
423
+  "putHtmlDoc": {
424
+    "author": {
425
+      "avatar_url": "/api/v2/assets/avatars/suri-cate.jpg",
426
+      "public_name": "Suri Cate",
427
+      "user_id": 3
428
+    },
429
+    "content_id": 6,
430
+    "content_type": "html-content",
431
+    "created": "2018-06-28T08:46:58.690Z",
432
+    "current_revision_id": 12,
433
+    "is_archived": false,
434
+    "is_deleted": false,
435
+    "label": "Intervention Report 12",
436
+    "last_modifier": {
437
+      "avatar_url": "/api/v2/assets/avatars/suri-cate.jpg",
438
+      "public_name": "Suri Cate",
439
+      "user_id": 3
440
+    },
441
+    "modified": "2018-06-28T08:46:58.690Z",
442
+    "parent_id": 34,
443
+    "raw_content": "string",
444
+    "show_in_ui": true,
445
+    "slug": "intervention-report-12",
446
+    "status": "closed-deprecated",
447
+    "sub_content_types": [
448
+      "string"
449
+    ],
450
+    "workspace_id": 19
451
+  }
423 452
 }

+ 2 - 2
src/action-creator.async.js View File

@@ -155,9 +155,9 @@ export const getWorkspaceList = idUser => dispatch => {
155 155
   })
156 156
 }
157 157
 
158
-export const getWorkspaceContentList = idWorkspace => dispatch => {
158
+export const getWorkspaceContentList = (idWorkspace, idParent) => dispatch => {
159 159
   return fetchWrapper({
160
-    url: `${FETCH_CONFIG.apiUrl}/workspaces/${idWorkspace}/contents`,
160
+    url: `${FETCH_CONFIG.apiUrl}/workspaces/${idWorkspace}/contents?parent_id=${idParent}`,
161 161
     param: {
162 162
       headers: {...FETCH_CONFIG.headers},
163 163
       method: 'GET'

+ 8 - 4
src/appFactory.js View File

@@ -7,21 +7,25 @@ export function appFactory (WrappedComponent) {
7 7
       loggedUser: user.logged ? user : {},
8 8
       config: {
9 9
         ...appConfig,
10
+        domContainer: 'appContainer',
10 11
         apiUrl: FETCH_CONFIG.apiUrl,
11
-        mockApiUrl: FETCH_CONFIG.mockApiUrl
12
+        mockApiUrl: FETCH_CONFIG.mockApiUrl,
13
+        apiHeader: FETCH_CONFIG.headers
12 14
       },
13 15
       content
14 16
     })
15 17
 
16
-    renderCreateContentApp = (appConfig, user, folder) => GLOBAL_renderCreateContentApp({
18
+    renderCreateContentApp = (appConfig, user, idWorkspace, idfolder) => GLOBAL_renderCreateContentApp({
17 19
       loggedUser: user.logged ? user : {},
18 20
       config: {
19 21
         ...appConfig,
20 22
         domContainer: 'popupCreateContentContainer',
21 23
         apiUrl: FETCH_CONFIG.apiUrl,
22
-        mockApiUrl: FETCH_CONFIG.mockApiUrl
24
+        mockApiUrl: FETCH_CONFIG.mockApiUrl,
25
+        apiHeader: FETCH_CONFIG.headers
23 26
       },
24
-      folder
27
+      idWorkspace,
28
+      idfolder
25 29
     })
26 30
 
27 31
     emitEventApp = (type, data) => GLOBAL_dispatchEvent(type, data)

+ 11 - 82
src/component/Workspace/Folder.jsx View File

@@ -2,8 +2,9 @@ import React from 'react'
2 2
 import { translate } from 'react-i18next'
3 3
 import PropTypes from 'prop-types'
4 4
 import classnames from 'classnames'
5
-import FileItem from './ContentItem.jsx'
5
+// import FileItem from './ContentItem.jsx'
6 6
 // import PopupExtandedAction from '../../container/PopupExtandedAction.jsx'
7
+import SubDropdownCreateButton from '../common/Input/SubDropdownCreateButton.jsx'
7 8
 import BtnExtandedAction from './BtnExtandedAction.jsx'
8 9
 
9 10
 class Folder extends React.Component {
@@ -26,11 +27,12 @@ class Folder extends React.Component {
26 27
 
27 28
   render () {
28 29
     const {
29
-      app,
30
+      availableApp,
30 31
       folderData,
31
-      onClickItem,
32
+      // onClickItem,
32 33
       onClickExtendedAction,
33
-      onClickFolder,
34
+      onClickCreateContent,
35
+      // onClickFolder,
34 36
       isLast,
35 37
       t
36 38
     } = this.props
@@ -66,85 +68,12 @@ class Folder extends React.Component {
66 68
                 {t('Folder.create')} ...
67 69
               </button>
68 70
 
69
-              {/* @TODO generate the subdropdown with available app from redux */}
70
-
71 71
               <div className='addbtn__subdropdown dropdown-menu' aria-labelledby='dropdownMenuButton'>
72
-                <div className='subdropdown__link dropdown-item' onClick={e => this.handleClickCreateContent(e, folderData, 'folder')}>
73
-                  <div className='subdropdown__link__folder d-flex align-items-center'>
74
-                    <div className='subdropdown__link__folder__icon mr-3'>
75
-                      <i className='fa fa-fw fa-folder-o' />
76
-                    </div>
77
-                    <div className='subdropdown__link__folder__text'>
78
-                      Créer un dossier
79
-                    </div>
80
-                  </div>
81
-                </div>
82
-
83
-                <div className='subdropdown__link dropdown-item' onClick={e => this.handleClickCreateContent(e, folderData, 'PageHtml')}>
84
-                  <div className='subdropdown__link__apphtml d-flex align-items-center'>
85
-                    <div className='subdropdown__link__apphtml__icon mr-3'>
86
-                      <i className='fa fa-fw fa-file-text-o' />
87
-                    </div>
88
-                    <div className='subdropdown__link__apphtml__text'>
89
-                      Rédiger un document
90
-                    </div>
91
-                  </div>
92
-                </div>
93
-
94
-                <div className='subdropdown__link dropdown-item' onClick={e => this.handleClickCreateContent(e, folderData, 'File')}>
95
-                  <div className='subdropdown__link__appfile d-flex align-items-center'>
96
-                    <div className='subdropdown__link__appfile__icon mr-3'>
97
-                      <i className='fa fa-fw fa-file-image-o' />
98
-                    </div>
99
-                    <div className='subdropdown__link__appfile__text'>
100
-                      Importer un fichier
101
-                    </div>
102
-                  </div>
103
-                </div>
104
-
105
-                <div className='subdropdown__link dropdown-item' onClick={e => this.handleClickCreateContent(e, folderData, 'PageMarkdown')}>
106
-                  <div className='subdropdown__link__appmarkdown d-flex align-items-center'>
107
-                    <div className='subdropdown__link__appmarkdown__icon mr-3'>
108
-                      <i className='fa fa-fw fa-file-code-o' />
109
-                    </div>
110
-                    <div className='subdropdown__link__appmarkdown__text'>
111
-                      Rédiger un document markdown
112
-                    </div>
113
-                  </div>
114
-                </div>
115
-
116
-                <div className='subdropdown__link dropdown-item' onClick={e => this.handleClickCreateContent(e, folderData, 'Thread')}>
117
-                  <div className='subdropdown__link__appthread d-flex align-items-center'>
118
-                    <div className='subdropdown__link__appthread__icon mr-3'>
119
-                      <i className='fa fa-fw fa-comments-o' />
120
-                    </div>
121
-                    <div className='subdropdown__link__appthread__text'>
122
-                      Lancer une discussion
123
-                    </div>
124
-                  </div>
125
-                </div>
126
-
127
-                <div className='subdropdown__link dropdown-item' onClick={e => this.handleClickCreateContent(e, folderData, 'Task')}>
128
-                  <div className='subdropdown__link__apptask d-flex align-items-center'>
129
-                    <div className='subdropdown__link__apptask__icon mr-3'>
130
-                      <i className='fa fa-fw fa-list-ul' />
131
-                    </div>
132
-                    <div className='subdropdown__link__apptask__text'>
133
-                      Créer une tâche
134
-                    </div>
135
-                  </div>
136
-                </div>
137
-
138
-                <div className='subdropdown__link dropdown-item' onClick={e => this.handleClickCreateContent(e, folderData, 'Issue')}>
139
-                  <div className='subdropdown__link__appissue d-flex align-items-center'>
140
-                    <div className='subdropdown__link__appissue__icon mr-3'>
141
-                      <i className='fa fa-fw fa-ticket' />
142
-                    </div>
143
-                    <div className='subdropdown__link__appissue__text'>
144
-                      Ouvrir un ticket
145
-                    </div>
146
-                  </div>
147
-                </div>
72
+                <SubDropdownCreateButton
73
+                  idFolder={null}
74
+                  availableApp={availableApp}
75
+                  onClickCreateContent={onClickCreateContent}
76
+                />
148 77
               </div>
149 78
 
150 79
               <div className='d-none d-md-flex'>

+ 10 - 71
src/component/common/Input/DropdownCreateButton.jsx View File

@@ -1,6 +1,7 @@
1 1
 import React from 'react'
2 2
 import PropTypes from 'prop-types'
3 3
 import classnames from 'classnames'
4
+import SubDropdownCreateButton from './SubDropdownCreateButton.jsx'
4 5
 
5 6
 const DropdownCreateButton = props => {
6 7
   return (
@@ -22,76 +23,11 @@ const DropdownCreateButton = props => {
22 23
         className={classnames(`${props.parentClass}__setting`, 'dropdownCreateBtn__setting dropdown-menu')}
23 24
         aria-labelledby='dropdownCreateBtn'
24 25
       >
25
-        <div className='setting__link dropdown-item'>
26
-          <div className='setting__link__folder d-flex align-items-center'>
27
-            <div className='setting__link__folder__icon mr-3'>
28
-              <i className='fa fa-fw fa-folder-o' />
29
-            </div>
30
-            <div className='setting__link__folder__text'>
31
-              Créer un dossier
32
-            </div>
33
-          </div>
34
-        </div>
35
-        <div className='setting__link dropdown-item'>
36
-          <div className='setting__link__apphtml d-flex align-items-center'>
37
-            <div className='setting__link__apphtml__icon mr-3'>
38
-              <i className='fa fa-fw fa-file-text-o' />
39
-            </div>
40
-            <div className='setting__link__apphtml__text'>
41
-              Rédiger un document
42
-            </div>
43
-          </div>
44
-        </div>
45
-        <div className='setting__link dropdown-item'>
46
-          <div className='setting__link__appfile d-flex align-items-center'>
47
-            <div className='setting__link__appfile__icon mr-3'>
48
-              <i className='fa fa-fw fa-file-image-o' />
49
-            </div>
50
-            <div className='setting__link__appfile__text'>
51
-              Importer un fichier
52
-            </div>
53
-          </div>
54
-        </div>
55
-        <div className='setting__link dropdown-item'>
56
-          <div className='setting__link__appmarkdown d-flex align-items-center'>
57
-            <div className='setting__link__appmarkdown__icon mr-3'>
58
-              <i className='fa fa-fw fa-file-code-o' />
59
-            </div>
60
-            <div className='setting__link__appmarkdown__text'>
61
-              Rédiger un document markdown
62
-            </div>
63
-          </div>
64
-        </div>
65
-        <div className='setting__link dropdown-item'>
66
-          <div className='setting__link__appthread d-flex align-items-center'>
67
-            <div className='setting__link__appthread__icon mr-3'>
68
-              <i className='fa fa-fw fa-comments-o' />
69
-            </div>
70
-            <div className='setting__link__appthread__text'>
71
-              Lancer une discussion
72
-            </div>
73
-          </div>
74
-        </div>
75
-        <div className='setting__link dropdown-item'>
76
-          <div className='setting__link__apptask d-flex align-items-center'>
77
-            <div className='setting__link__apptask__icon mr-3'>
78
-              <i className='fa fa-fw fa-list-ul' />
79
-            </div>
80
-            <div className='setting__link__apptask__text'>
81
-              Créer une tâche
82
-            </div>
83
-          </div>
84
-        </div>
85
-        <div className='setting__link dropdown-item'>
86
-          <div className='setting__link__appissue d-flex align-items-center'>
87
-            <div className='setting__link__appissue__icon mr-3'>
88
-              <i className='fa fa-fw fa-ticket' />
89
-            </div>
90
-            <div className='setting__link__appissue__text'>
91
-              Ouvrir un ticket
92
-            </div>
93
-          </div>
94
-        </div>
26
+        <SubDropdownCreateButton
27
+          idFolder={null}
28
+          availableApp={props.availableApp}
29
+          onClickCreateContent={props.onClickCreateContent}
30
+        />
95 31
       </div>
96 32
     </div>
97 33
   )
@@ -100,8 +36,11 @@ const DropdownCreateButton = props => {
100 36
 export default DropdownCreateButton
101 37
 
102 38
 DropdownCreateButton.propTypes = {
39
+  availableApp: PropTypes.array.isRequired,
40
+  onClickCreateContent: PropTypes.func.isRequired,
103 41
   parentClass: PropTypes.string,
104
-  customClass: PropTypes.string
42
+  customClass: PropTypes.string,
43
+  idFolder: PropTypes.number
105 44
 }
106 45
 
107 46
 DropdownCreateButton.defaultProps = {

+ 31 - 0
src/component/common/Input/SubDropdownCreateButton.jsx View File

@@ -0,0 +1,31 @@
1
+import React from 'react'
2
+import PropTypes from 'prop-types'
3
+
4
+require('./SubDropdownCreateButton.styl')
5
+
6
+const SubDropdownCreateButton = props => {
7
+  return (
8
+    <div>
9
+      {props.availableApp.map(app =>
10
+        <div className='subdropdown__link dropdown-item' onClick={e => props.onClickCreateContent(e, props.idFolder, app.slug)} key={app.slug}>
11
+          <div className={`subdropdown__link__${app.slug} d-flex align-items-center`}>
12
+            <div className={`subdropdown__link__${app.slug}__icon mr-3`}>
13
+              <i className={`fa fa-fw fa-${app.faIcon}`} />
14
+            </div>
15
+            <div className='subdropdown__link__folder__text'>
16
+              {app.creationLabel}
17
+            </div>
18
+          </div>
19
+        </div>
20
+      )}
21
+    </div>
22
+  )
23
+}
24
+
25
+SubDropdownCreateButton.propTypes = {
26
+  availableApp: PropTypes.array.isRequired,
27
+  onClickCreateContent: PropTypes.func.isRequired,
28
+  idFolder: PropTypes.number
29
+}
30
+
31
+export default SubDropdownCreateButton

+ 2 - 0
src/component/common/Input/SubDropdownCreateButton.styl View File

@@ -0,0 +1,2 @@
1
+.subdropdown__link
2
+  padding 10px

+ 56 - 0
src/container/OpenContentApp.jsx View File

@@ -0,0 +1,56 @@
1
+import React from 'react'
2
+import { connect } from 'react-redux'
3
+import { withRouter } from 'react-router'
4
+import appFactory from '../appFactory.js'
5
+
6
+export class OpenContentApp extends React.Component {
7
+  openContentApp = () => {
8
+    const { idWorkspace, appOpened, user, workspaceContent, contentType, renderApp, match } = this.props
9
+
10
+    if (isNaN(idWorkspace)) return
11
+
12
+    if (['type', 'idcts'].every(p => p in match.params) && match.params.type !== 'contents' && workspaceContent.id !== -1 && workspaceContent.length) {
13
+      // @TODO test validity of params, idcts isNaN and type includes list of available content type
14
+      const contentToOpen = {
15
+        content_id: parseInt(match.params.idcts),
16
+        workspace_id: parseInt(idWorkspace),
17
+        type: match.params.type
18
+      }
19
+
20
+      console.log('contentToOpen', contentToOpen)
21
+
22
+      if (!appOpened) {
23
+        renderApp(
24
+          contentType.find(ct => ct.slug === contentToOpen.type),
25
+          user,
26
+          contentToOpen
27
+        )
28
+        this.props.updateAppOpened(true)
29
+      } else {
30
+        GLOBAL_dispatchEvent({
31
+          type: 'html-documents_reloadContent',
32
+          data: contentToOpen
33
+        })
34
+      }
35
+    }
36
+  }
37
+
38
+  componentDidMount () {
39
+    console.log('OpenContentApp did Mount', this.props)
40
+
41
+    this.openContentApp()
42
+  }
43
+
44
+  componentDidUpdate () {
45
+    console.log('OpenContentApp did Update', this.props)
46
+
47
+    this.openContentApp()
48
+  }
49
+
50
+  render () {
51
+    return null
52
+  }
53
+}
54
+
55
+const mapStateToProps = ({ user, workspaceContent, contentType }) => ({ user, workspaceContent, contentType })
56
+export default withRouter(connect(mapStateToProps)(appFactory(OpenContentApp)))

+ 1 - 1
src/container/Sidebar.jsx View File

@@ -59,7 +59,7 @@ class Sidebar extends React.Component {
59 59
 
60 60
           <nav className='sidebar__navigation'>
61 61
             <ul className='sidebar__navigation__workspace'>
62
-              { workspaceList.map((ws, i) =>
62
+              { workspaceList.map(ws =>
63 63
                 <WorkspaceListItem
64 64
                   idWs={ws.id}
65 65
                   label={ws.label}

+ 6 - 6
src/container/Tracim.jsx View File

@@ -16,7 +16,6 @@ import {
16 16
 import PrivateRoute from './PrivateRoute.jsx'
17 17
 import { PAGE } from '../helper.js'
18 18
 import {
19
-  getLangList,
20 19
   getUserIsConnected
21 20
 } from '../action-creator.async.js'
22 21
 import {
@@ -28,8 +27,6 @@ class Tracim extends React.Component {
28 27
   async componentDidMount () {
29 28
     const { dispatch } = this.props
30 29
 
31
-    dispatch(getLangList())
32
-
33 30
     const fetchGetUserIsConnected = await dispatch(getUserIsConnected())
34 31
     switch (fetchGetUserIsConnected.status) {
35 32
       case 200:
@@ -54,14 +51,17 @@ class Tracim extends React.Component {
54 51
         { user.logged === undefined
55 52
           ? (<div />) // while we dont know if user is connected, display nothing but the header @TODO show loader
56 53
           : (
57
-            <div className='tracim__content'>
54
+            <div className='tracim__content'> {/* uses of <Switch> component in react router ? */}
58 55
               <Route path={PAGE.LOGIN} component={Login} />
59 56
 
60 57
               <PrivateRoute exact path={PAGE.HOME} component={WorkspaceContent} />
61 58
               <PrivateRoute path={PAGE.ACCOUNT} component={Account} />
62 59
               {/* bellow, the '?' is important, it avoid to have to declare another route for CONTENT_LIST which could double match */}
63
-              <PrivateRoute path={PAGE.WORKSPACE.CONTENT(':idws', ':idcts?')} component={WorkspaceContent} />
64
-              <PrivateRoute exact path={PAGE.WORKSPACE.DASHBOARD()} component={Dashboard} />
60
+              {/* <PrivateRoute path={PAGE.WORKSPACE.CONTENT(':idws', ':type?', ':idcts?')} component={WorkspaceContent} /> */}
61
+
62
+              <Route path='/workspaces/:idws/' component={WorkspaceContent} />
63
+
64
+              <PrivateRoute exact path={PAGE.WORKSPACE.DASHBOARD(':idws')} component={Dashboard} />
65 65
               <PrivateRoute path={'/wip/:cp'} component={WIPcomponent} /> {/* for testing purpose only */}
66 66
 
67 67
               <Footer />

+ 46 - 41
src/container/WorkspaceContent.jsx View File

@@ -1,6 +1,6 @@
1 1
 import React from 'react'
2 2
 import { connect } from 'react-redux'
3
-import { withRouter } from 'react-router'
3
+import { withRouter, Route } from 'react-router-dom'
4 4
 import appFactory from '../appFactory.js'
5 5
 import { PAGE } from '../helper.js'
6 6
 import Sidebar from './Sidebar.jsx'
@@ -11,6 +11,7 @@ import PageWrapper from '../component/common/layout/PageWrapper.jsx'
11 11
 import PageTitle from '../component/common/layout/PageTitle.jsx'
12 12
 import PageContent from '../component/common/layout/PageContent.jsx'
13 13
 import DropdownCreateButton from '../component/common/Input/DropdownCreateButton.jsx'
14
+import OpenContentApp from './OpenContentApp.jsx'
14 15
 import {
15 16
   getAppList,
16 17
   getContentTypeList,
@@ -39,21 +40,22 @@ class WorkspaceContent extends React.Component {
39 40
         type: undefined,
40 41
         folder: undefined
41 42
       },
42
-      workspaceIdInUrl: props.match.params.idws ? parseInt(props.match.params.idws) : null,
43
-      workspaceOpened: false
43
+      workspaceIdInUrl: props.match.params.idws ? parseInt(props.match.params.idws) : null, // this is used to avoid handling the parseInt everytime
44
+      appOpened: false
44 45
     }
45 46
 
46 47
     document.addEventListener('appCustomEvent', this.customEventReducer)
47 48
   }
48 49
 
49 50
   customEventReducer = ({ detail: { type, data } }) => {
51
+    console.log(type, data)
50 52
     switch (type) {
51 53
       case 'openContentUrl':
52
-        this.props.history.push(PAGE.WORKSPACE.CONTENT(data.idWorkspace, data.idContent))
54
+        this.props.history.push(PAGE.WORKSPACE.CONTENT(data.idWorkspace, data.contentType, data.idContent))
53 55
         break
54 56
       case 'appClosed':
55
-        this.props.history.push(PAGE.WORKSPACE.CONTENT(this.props.workspace.id, ''))
56
-        this.setState({workspaceOpened: false})
57
+        this.props.history.push(PAGE.WORKSPACE.CONTENT_LIST(this.state.workspaceIdInUrl))
58
+        this.setState({appOpened: false})
57 59
         break
58 60
     }
59 61
   }
@@ -82,58 +84,39 @@ class WorkspaceContent extends React.Component {
82 84
 
83 85
       if (fetchGetWorkspaceList.status === 200) {
84 86
         dispatch(updateWorkspaceListData(fetchGetWorkspaceList.json))
85
-        dispatch(setWorkspaceListIsOpenInSidebar(workspaceIdInUrl || fetchGetWorkspaceList.json[0].id, true))
87
+        dispatch(setWorkspaceListIsOpenInSidebar(workspaceIdInUrl || fetchGetWorkspaceList.json[0].workspace_id, true))
86 88
 
87 89
         if (match.params.idws === undefined && fetchGetWorkspaceList.json.length > 0) {
88
-          wsToLoad = fetchGetWorkspaceList.json[0].id // load first ws if none specified
90
+          wsToLoad = fetchGetWorkspaceList.json[0].workspace_id // load first ws if none specified
89 91
         }
90 92
       }
91 93
     }
92 94
 
93 95
     if (wsToLoad === null) return // ws already loaded
94 96
 
95
-    const wsContent = await dispatch(getWorkspaceContentList(wsToLoad))
97
+    const wsContent = await dispatch(getWorkspaceContentList(wsToLoad, 0))
96 98
 
97 99
     if (wsContent.status === 200) dispatch(setWorkspaceContent(wsContent.json, qs.parse(location.search).type))
98 100
     else dispatch(newFlashMessage('Error while loading workspace', 'danger'))
99 101
   }
100 102
 
101 103
   componentDidUpdate (prevProps, prevState) {
102
-    const { contentType, workspaceContent, user, renderApp, match } = this.props
103
-
104 104
     console.log('componentDidUpdate')
105 105
 
106 106
     if (this.state.workspaceIdInUrl === null) return
107 107
 
108
-    const idWorkspace = parseInt(match.params.idws)
109
-    if (prevState.workspaceIdInUrl !== idWorkspace) this.setState({workspaceIdInUrl: idWorkspace})
110
-
111
-    // if (user.user_id !== -1 && prevProps.user.id !== user.id) dispatch(getWorkspaceList(user.user_id, idWorkspace))
108
+    const idWorkspace = parseInt(this.props.match.params.idws)
112 109
 
113
-    if (match.params.idcts && workspaceContent.id !== -1 && !workspaceOpened && workspaceContent.length) { // if a content id is in url, open it
114
-      const idContentToOpen = parseInt(match.params.idcts)
115
-      const contentToOpen = workspaceContent.find(wsc => wsc.id === idContentToOpen) // || await dispatch(getWorkspaceContent(idWorkspace, idContentToOpen))
110
+    if (isNaN(idWorkspace)) return
116 111
 
117
-      // @FIXME : for alpha, we do not handle subfolder. commented code bellow should load a component that is not in the workspace root
118
-      // if (contentToOpen === undefined) { // content is not is ws root
119
-      //   const fetchContent = await dispatch(getWorkspaceContent(idWorkspace, idContentToOpen))
120
-      //   console.log(fetchContent)
121
-      // }
122
-
123
-      console.log('contentToOpen', contentToOpen)
112
+    if (prevState.workspaceIdInUrl !== idWorkspace) this.setState({workspaceIdInUrl: idWorkspace})
124 113
 
125
-      renderApp(
126
-        contentType.find(ct => ct.type === contentToOpen.type),
127
-        user,
128
-        {...contentToOpen, workspaceContent: workspaceContent}
129
-      )
130
-      this.setState({workspaceOpened: true})
131
-    }
114
+    // if (user.user_id !== -1 && prevProps.user.id !== user.id) dispatch(getWorkspaceList(user.user_id, idWorkspace))
132 115
   }
133 116
 
134 117
   handleClickContentItem = content => {
135 118
     console.log('content clicked', content)
136
-    this.props.history.push(`${PAGE.WORKSPACE.CONTENT(content.workspaceId, content.id)}${this.props.location.search}`)
119
+    this.props.history.push(`/workspaces/${content.idWorkspace}/${content.type}/${content.id}`)
137 120
   }
138 121
 
139 122
   handleClickEditContentItem = (e, content) => {
@@ -165,15 +148,22 @@ class WorkspaceContent extends React.Component {
165 148
     this.props.dispatch(getFolderContent(this.props.workspace.id, folderId))
166 149
   }
167 150
 
168
-  handleClickCreateContent = (folder, contentType) => {
169
-    this.props.renderCreateContentApp(this.props.app[contentType], this.props.user, folder)
151
+  handleClickCreateContent = (e, idFolder, contentType) => {
152
+    e.stopPropagation()
153
+    this.props.renderCreateContentApp(
154
+      this.props.contentType.find(ct => ct.slug === contentType),
155
+      this.props.user,
156
+      this.props.match.params.idws,
157
+      idFolder
158
+    )
170 159
   }
171 160
 
161
+  handleUpdateAppOpened = opened => this.setState({appOpened: opened})
162
+
172 163
   render () {
173
-    const { workspaceContent, app, contentType } = this.props
164
+    const { workspaceContent, contentType, match } = this.props
174 165
 
175 166
     const filterWorkspaceContent = (contentList, filter) => {
176
-      console.log(contentList, filter)
177 167
       return filter.length === 0
178 168
         ? contentList
179 169
         : contentList.filter(c => c.type === 'folder' || filter.includes(c.type)) // keep unfiltered files and folders
@@ -187,19 +177,28 @@ class WorkspaceContent extends React.Component {
187 177
     const filteredWorkspaceContent = workspaceContent.length > 0
188 178
       ? filterWorkspaceContent(workspaceContent, urlFilter ? [urlFilter] : [])
189 179
       : []
190
-    console.log('workspaceContent => filteredWorkspaceContent', filteredWorkspaceContent)
180
+    console.log('workspaceContent => filteredWorkspaceContent', filteredWorkspaceContent, 'urlFilter', urlFilter)
191 181
 
192 182
     return (
193 183
       <div className='sidebarpagecontainer'>
194 184
         <Sidebar />
195 185
 
186
+        <Route path={`${match.url}/:type/:idcts`} render={() =>
187
+          <OpenContentApp idWorkspace={match.params.idws} appOpened={this.state.appOpened} updateAppOpened={this.handleUpdateAppOpened} />}
188
+        />
189
+
196 190
         <PageWrapper customeClass='workspace'>
197 191
           <PageTitle
198 192
             parentClass='workspace__header'
199 193
             customClass='justify-content-between'
200 194
             title={workspaceContent.label ? workspaceContent.label : ''}
201 195
           >
202
-            <DropdownCreateButton parentClass='workspace__header__btnaddworkspace' />
196
+            <DropdownCreateButton
197
+              parentClass='workspace__header__btnaddworkspace'
198
+              idFolder={null}
199
+              onClickCreateContent={this.handleClickCreateContent}
200
+              availableApp={contentType}
201
+            />
203 202
           </PageTitle>
204 203
 
205 204
           <PageContent parentClass='workspace__content'>
@@ -211,7 +210,7 @@ class WorkspaceContent extends React.Component {
211 210
               { filteredWorkspaceContent.map((c, i) => c.type === 'folder'
212 211
                 ? (
213 212
                   <Folder
214
-                    app={app}
213
+                    availableApp={contentType}
215 214
                     folderData={c}
216 215
                     onClickItem={this.handleClickContentItem}
217 216
                     onClickExtendedAction={{
@@ -242,6 +241,7 @@ class WorkspaceContent extends React.Component {
242 241
                       archive: e => this.handleClickArchiveContentItem(e, c),
243 242
                       delete: e => this.handleClickDeleteContentItem(e, c)
244 243
                     }}
244
+                    onClickCreateContent={this.handleClickCreateContent}
245 245
                     isLast={i === filteredWorkspaceContent.length - 1}
246 246
                     key={c.id}
247 247
                   />
@@ -249,7 +249,12 @@ class WorkspaceContent extends React.Component {
249 249
               )}
250 250
             </div>
251 251
 
252
-            <DropdownCreateButton customClass='workspace__content__button mb-5' />
252
+            <DropdownCreateButton
253
+              customClass='workspace__content__button mb-5'
254
+              idFolder={null}
255
+              onClickCreateContent={this.handleClickCreateContent}
256
+              availableApp={contentType}
257
+            />
253 258
 
254 259
             <div id='appContainer' />
255 260
           </PageContent>

+ 0 - 2
src/css/Folder.styl View File

@@ -120,8 +120,6 @@ folderopen()
120 120
               margin-left 20px
121 121
           &__subdropdown
122 122
             padding 0
123
-            .subdropdown__link
124
-              padding 10px
125 123
     &__status
126 124
       flex-grow 0
127 125
       padding 0 15px

+ 2 - 2
src/helper.js View File

@@ -11,11 +11,11 @@ export const FETCH_CONFIG = {
11 11
 export const PAGE = {
12 12
   HOME: '/',
13 13
   WORKSPACE: {
14
-    DASHBOARD: (idws = ':idws') => `/workspaces/${idws}`,
14
+    DASHBOARD: (idws = ':idws') => `/workspaces/${idws}/dashboard`,
15 15
     NEW: '/workspaces/new',
16 16
     CALENDAR: (idws = ':idws') => `/workspaces/${idws}/calendar`,
17 17
     CONTENT_LIST: (idws = ':idws') => `/workspaces/${idws}/contents`,
18
-    CONTENT: (idws = ':idws', idcts = ':idcts') => `/workspaces/${idws}/contents/${idcts}`,
18
+    CONTENT: (idws = ':idws', type = ':type', idcts = ':idcts') => `/workspaces/${idws}/${type}/${idcts}`,
19 19
     CONTENT_NEW: (idws = ':idws', ctstype = ':ctstype') => `/workspaces/${idws}/contents/${ctstype}/new`,
20 20
     CONTENT_EDIT: (idws = ':idws', idcts = ':idcts') => `/workspaces/${idws}/contents/${idcts}/edit`,
21 21
     CONTENT_TITLE_EDIT: (idws = ':idws', idcts = ':idcts') => `/workspaces/${idws}/contents/${idcts}/title/edit`,

+ 1 - 1
src/index.js View File

@@ -11,7 +11,7 @@ require('./css/index.styl')
11 11
 
12 12
 ReactDOM.render(
13 13
   <Provider store={store}>
14
-    <BrowserRouter basename={'/#'}>
14
+    <BrowserRouter>
15 15
       <I18nextProvider i18n={i18n}>
16 16
         <Tracim />
17 17
       </I18nextProvider>

+ 4 - 4
src/reducer/workspaceContent.js View File

@@ -7,16 +7,16 @@ export default function workspace (state = [], action) {
7 7
   switch (action.type) {
8 8
     case `Set/${WORKSPACE}/Content`:
9 9
       return action.workspaceContent.map(wsc => ({
10
-        id: wsc.id,
10
+        id: wsc.content_id,
11 11
         label: wsc.label,
12 12
         slug: wsc.slug,
13
-        type: wsc.content_type_slug,
14
-        workspaceId: wsc.workspace_id,
13
+        type: wsc.content_type,
14
+        idWorkspace: wsc.workspace_id,
15 15
         isArchived: wsc.is_archived,
16 16
         parentId: wsc.parent_id,
17 17
         isDeleted: wsc.is_deleted,
18 18
         showInUi: wsc.show_in_ui,
19
-        statusSlug: wsc.status_slug,
19
+        statusSlug: wsc.status,
20 20
         subContentTypeSlug: wsc.sub_content_type_slug
21 21
       }))
22 22
 

+ 1 - 1
src/reducer/workspaceList.js View File

@@ -7,7 +7,7 @@ export function workspaceList (state = [], action) {
7 7
   switch (action.type) {
8 8
     case `Update/${WORKSPACE_LIST}`:
9 9
       return action.workspaceList.map(ws => ({
10
-        id: ws.id,
10
+        id: ws.workspace_id,
11 11
         label: ws.label,
12 12
         slug: ws.slug,
13 13
         description: ws.description,