Browse Source

integration of account page

Skylsmoi 6 years ago
parent
commit
c86df9953a

+ 5 - 0
jsonserver/server.js View File

@@ -1,5 +1,6 @@
1 1
 const jsonServer = require('json-server')
2 2
 const jsonDb = require('./static_db.json')
3
+const timezoneDb = require('./timezone.json')
3 4
 const server = jsonServer.create()
4 5
 const router = jsonServer.router() // for persistence : jsonServer.router('static_db.json')
5 6
 const middlewares = jsonServer.defaults()
@@ -55,6 +56,10 @@ server.get('/workspace/:id', (req, res) => res.jsonp(
55 56
   Object.assign({}, jsonDb.workspace_detail, {content: shuffle(jsonDb.workspace_detail.content)})
56 57
 ))
57 58
 
59
+server.get('/user/:id/roles', (req, res) => res.jsonp(jsonDb.user_role))
60
+
61
+server.get('/timezone', (req, res) => res.jsonp(timezoneDb.timezone))
62
+
58 63
 server.get('/workspace/:idws/content/:idc', (req, res) => {
59 64
   switch (req.params.idc) {
60 65
     case '1': // pageHtml

+ 48 - 1
jsonserver/static_db.json View File

@@ -18,7 +18,11 @@
18 18
       "firstname": "Côme",
19 19
       "lastname": "Stoilenom",
20 20
       "email": "osef@algoo.fr",
21
-      "avatar": "https://avatars3.githubusercontent.com/u/11177014?s=460&v=4"
21
+      "avatar": "https://avatars3.githubusercontent.com/u/11177014?s=460&v=4",
22
+      "role": "Utilisateur",
23
+      "job": "Integrateur | Webdesigner",
24
+      "company": "Algoo",
25
+      "caldav_url": "http://algoo.trac.im/caldav/user/5.ics/"
22 26
     }
23 27
   },
24 28
   "app_config": [{
@@ -289,5 +293,48 @@
289 293
   }, {
290 294
     "id": 5,
291 295
     "title": "Évolutions"
296
+  }],
297
+  "user_role": [{
298
+    "workspace": {
299
+      "id": 0,
300
+      "title": "Ressources humaine"
301
+    },
302
+    "role": "reader",
303
+    "subscribed_to_notif": true
304
+  }, {
305
+    "workspace": {
306
+      "id": 1,
307
+      "title": "Marketing"
308
+    },
309
+    "role": "manager",
310
+    "subscribed_to_notif": true
311
+  }, {
312
+    "workspace": {
313
+      "id": 2,
314
+      "title": "Missions externes"
315
+    },
316
+    "role": "contributor",
317
+    "subscribed_to_notif": false
318
+  }, {
319
+    "workspace": {
320
+      "id": 3,
321
+      "title": "Recherche et développement"
322
+    },
323
+    "role": "content_manager",
324
+    "subscribed_to_notif": true
325
+  }, {
326
+    "workspace": {
327
+      "id": 4,
328
+      "title": "Commercialisation"
329
+    },
330
+    "role": "reader",
331
+    "subscribed_to_notif": false
332
+  }, {
333
+    "workspace": {
334
+      "id": 5,
335
+      "title": "Évolutions"
336
+    },
337
+    "role": "content_manager",
338
+    "subscribed_to_notif": true
292 339
   }]
293 340
 }

+ 0 - 0
jsonserver/timezone.json View File


+ 27 - 3
src/action-creator.async.js View File

@@ -1,12 +1,16 @@
1 1
 import { FETCH_CONFIG } from './helper.js'
2 2
 import {
3
+  TIMEZONE,
4
+  setTimezone,
3 5
   LANG,
4 6
   updateLangList,
5 7
   USER_LOGIN,
6 8
   USER_DATA,
9
+  USER_ROLE,
7 10
   USER_CONNECTED,
8
-  updateUserConnected,
11
+  setUserConnected,
9 12
   updateUserData,
13
+  setUserRole,
10 14
   WORKSPACE,
11 15
   updateWorkspaceData,
12 16
   WORKSPACE_LIST,
@@ -71,6 +75,16 @@ export const getLangList = () => async dispatch => {
71 75
   if (fetchGetLangList.status === 200) dispatch(updateLangList(fetchGetLangList.json))
72 76
 }
73 77
 
78
+export const getTimezone = () => async dispatch => {
79
+  const fetchGetTimezone = await fetchWrapper({
80
+    url: `${FETCH_CONFIG.apiUrl}/timezone`,
81
+    param: {...FETCH_CONFIG.header, method: 'GET'},
82
+    actionName: TIMEZONE,
83
+    dispatch
84
+  })
85
+  if (fetchGetTimezone.status === 200) dispatch(setTimezone(fetchGetTimezone.json))
86
+}
87
+
74 88
 export const userLogin = (login, password, rememberMe) => async dispatch => {
75 89
   const fetchUserLogin = await fetchWrapper({
76 90
     url: `${FETCH_CONFIG.apiUrl}/user/login`,
@@ -86,7 +100,7 @@ export const userLogin = (login, password, rememberMe) => async dispatch => {
86 100
     actionName: USER_LOGIN,
87 101
     dispatch
88 102
   })
89
-  if (fetchUserLogin.status === 200) dispatch(updateUserConnected(fetchUserLogin.json))
103
+  if (fetchUserLogin.status === 200) dispatch(setUserConnected(fetchUserLogin.json))
90 104
 }
91 105
 
92 106
 export const getIsUserConnected = () => async dispatch => {
@@ -96,7 +110,17 @@ export const getIsUserConnected = () => async dispatch => {
96 110
     actionName: USER_CONNECTED,
97 111
     dispatch
98 112
   })
99
-  if (fetchUserLogged.status === 200) dispatch(updateUserConnected(fetchUserLogged.json))
113
+  if (fetchUserLogged.status === 200) dispatch(setUserConnected(fetchUserLogged.json))
114
+}
115
+
116
+export const getUserRole = user => async dispatch => {
117
+  const fetchGetUserRole = await fetchWrapper({
118
+    url: `${FETCH_CONFIG.apiUrl}/user/${user.id}/roles`,
119
+    param: {...FETCH_CONFIG.header, method: 'GET'},
120
+    actionName: USER_ROLE,
121
+    dispatch
122
+  })
123
+  if (fetchGetUserRole.status === 200) dispatch(setUserRole(fetchGetUserRole.json))
100 124
 }
101 125
 
102 126
 export const updateUserLang = newLang => async dispatch => { // unused

+ 9 - 3
src/action-creator.sync.js View File

@@ -1,16 +1,22 @@
1
+export const TIMEZONE = 'Timezone'
2
+export const setTimezone = timezone => ({ type: `Set/${TIMEZONE}`, timezone })
3
+
1 4
 export const USER_LOGIN = 'User/Login'
2 5
 export const USER_DATA = 'User/Data'
3
-
6
+export const USER_ROLE = 'User/Role'
4 7
 export const USER_CONNECTED = 'User/Connected'
5
-export const updateUserConnected = user => ({ type: `Update/${USER_CONNECTED}`, user })
8
+export const setUserConnected = user => ({ type: `Set/${USER_CONNECTED}`, user })
6 9
 export const updateUserData = userData => ({ type: `Update/${USER_DATA}`, data: userData })
10
+export const setUserRole = userRole => ({ type: `Set/${USER_ROLE}`, userRole }) // this actually update workspaceList state
11
+export const updateUserWorkspaceSubscriptionNotif = (workspaceId, subscriptionNotif) =>
12
+  ({ type: `Update/${USER_ROLE}/SubscriptionNotif`, workspaceId, subscriptionNotif })
7 13
 
8 14
 export const WORKSPACE = 'Workspace'
9 15
 export const updateWorkspaceData = workspace => ({ type: `Update/${WORKSPACE}`, workspace })
10 16
 
11 17
 export const WORKSPACE_LIST = 'WorkspaceList'
12 18
 export const updateWorkspaceListData = workspaceList => ({ type: `Update/${WORKSPACE_LIST}`, workspaceList })
13
-export const setWorkspaceListIsOpen = (workspaceId, isOpen) => ({ type: `Set/${WORKSPACE_LIST}/isOpen`, workspaceId, isOpen })
19
+export const setWorkspaceListisOpenInSidebar = (workspaceId, isOpenInSidebar) => ({ type: `Set/${WORKSPACE_LIST}/isOpenInSidebar`, workspaceId, isOpenInSidebar })
14 20
 
15 21
 export const FILE_CONTENT = 'FileContent'
16 22
 export const setActiveFileContentActive = file => ({ type: `Set/${FILE_CONTENT}/Active`, file })

+ 38 - 0
src/component/Account/Calendar.jsx View File

@@ -0,0 +1,38 @@
1
+import React from 'react'
2
+
3
+export const Calendar = props => {
4
+  return (
5
+    <div className='account__userpreference__setting__calendar'>
6
+
7
+      <div className='calendar__title subTitle ml-2 ml-sm-0'>
8
+        Calendrier
9
+      </div>
10
+
11
+      <div className='calendar__text ml-2 ml-sm-0'>
12
+        NYI
13
+      </div>
14
+
15
+      <div className='calendar__title ml-2 ml-sm-0'>
16
+        Accèder à votre Calendrier personnel
17
+      </div>
18
+      <div className='calendar__link ml-2 ml-sm-0'>
19
+        {props.user.caldavUrl}
20
+      </div>
21
+
22
+      <div className='calendar__title ml-2 ml-sm-0'>
23
+        Changer de Fuseau Horaire :
24
+      </div>
25
+
26
+      <div className='calendar__timezone ml-2 ml-sm-0 dropdown'>
27
+        <button className='calendar__timezone__btn btn dropdown-toggle' type='button' id='dropdownMenuButton' data-toggle='dropdown' aria-haspopup='true' aria-expanded='false'>
28
+          Fuseau Horaire
29
+        </button>
30
+        <div className='calendar__timezone__submenu dropdown-menu'>
31
+          {props.timezone.map((t, i) => <div className='calendar__timezone__submenu__item dropdown-item' key={i}>{t.place} {t.gmt}</div>)}
32
+        </div>
33
+      </div>
34
+    </div>
35
+  )
36
+}
37
+
38
+export default Calendar

+ 33 - 0
src/component/Account/MenuSubComponent.jsx View File

@@ -0,0 +1,33 @@
1
+import React from 'react'
2
+import classnames from 'classnames'
3
+
4
+export const Navbar = props => {
5
+  return (
6
+    <nav className='account__userpreference__menu navbar d-flex align-items-start'>
7
+
8
+      <div className='account__userpreference__menu__responsive d-lg-none'>
9
+        <i className='fa fa-bars' />
10
+      </div>
11
+
12
+      <ul className='account__userpreference__menu__list nav flex-column'>
13
+
14
+        <li className='account__userpreference__menu__list__close nav-link'>
15
+          <i className='fa fa-times' />
16
+        </li>
17
+
18
+        <li className='account__userpreference__menu__list__disabled'>Menu</li>
19
+        { props.subMenuList.map(sm =>
20
+          <li
21
+            className={classnames('account__userpreference__menu__list__item nav-item', {'active': sm.active})}
22
+            onClick={() => props.onClickMenuItem(sm.name)}
23
+            key={sm.name}
24
+          >
25
+            <div className='account__userpreference__menu__list__item__link nav-link'>{sm.menuLabel}</div>
26
+          </li>
27
+        )}
28
+      </ul>
29
+    </nav>
30
+  )
31
+}
32
+
33
+export default Navbar

+ 60 - 0
src/component/Account/Notification.jsx View File

@@ -0,0 +1,60 @@
1
+import React from 'react'
2
+import { translate } from 'react-i18next'
3
+import { BtnSwitch } from 'tracim_lib'
4
+import { ROLE } from '../../helper.js'
5
+
6
+export const Notification = props => {
7
+  const getRole = role => ROLE.find(r => r.name === role)
8
+
9
+  return (
10
+    <div className='account__userpreference__setting__notification'>
11
+      <div className='notification__title subTitle ml-2 ml-sm-0'>
12
+        Mes Espaces de Travail
13
+      </div>
14
+
15
+      <div className='notification__text ml-2 ml-sm-0'>
16
+        NYI
17
+      </div>
18
+
19
+      <div className='notification__table'>
20
+        <table className='table'>
21
+          <thead>
22
+            <tr>
23
+              <th>Espace de travail</th>
24
+              <th>Role</th>
25
+              <th>Notification</th>
26
+            </tr>
27
+          </thead>
28
+          <tbody>
29
+
30
+            { props.workspaceList.map(ws =>
31
+              <tr key={ws.id}>
32
+                <td>
33
+                  <div className='notification__table__wksname'>
34
+                    {ws.title}
35
+                  </div>
36
+                </td>
37
+                <td>
38
+                  <div className='notification__table__role'>
39
+                    <div className='notification__table__role__icon'>
40
+                      <i className={`fa ${getRole(ws.role).icon}`} />
41
+                    </div>
42
+                    <div className='notification__table__role__text d-none d-sm-flex'>
43
+                      {props.t(getRole(ws.role).translationKey)}
44
+                    </div>
45
+                  </div>
46
+                </td>
47
+                <td>
48
+                  <BtnSwitch checked={ws.notif} onChange={() => props.onChangeSubscriptionNotif(ws.id, !ws.notif)} />
49
+                </td>
50
+              </tr>
51
+            )}
52
+
53
+          </tbody>
54
+        </table>
55
+      </div>
56
+    </div>
57
+  )
58
+}
59
+
60
+export default translate()(Notification)

+ 39 - 0
src/component/Account/PersonalData.jsx View File

@@ -0,0 +1,39 @@
1
+import React from 'react'
2
+
3
+export const PersonalData = props => {
4
+  return (
5
+    <div className='account__userpreference__setting__personaldata'>
6
+      <div className='personaldata__title subTitle ml-2 ml-sm-0'>
7
+        Mes informations personnelles
8
+      </div>
9
+
10
+      <div className='personaldata__text ml-2 ml-sm-0'>
11
+        NYI
12
+      </div>
13
+
14
+      <div className='personaldata__form'>
15
+        <div className='personaldata__form__title'>
16
+          Changer le mot de passe :
17
+        </div>
18
+        <input className='personaldata__form__txtinput form-control' type='password' placeholder='Ancien mot de passe' />
19
+        <input className='personaldata__form__txtinput form-control' type='password' placeholder='Nouveau mot de passe' />
20
+        <div className='personaldata__form__button btn btn-primary'>
21
+          Envoyer
22
+        </div>
23
+      </div>
24
+
25
+      <div className='personaldata__form'>
26
+        <div className='personaldata__form__title'>
27
+          Changer d'adresse mail :
28
+        </div>
29
+        <input className='personaldata__form__txtinput form-control' type='email' placeholder='Ancienne adresse mail' />
30
+        <input className='personaldata__form__txtinput form-control' type='email' placeholder='Nouvelle adresse mail' />
31
+        <div className='personaldata__form__button btn btn-primary'>
32
+          Envoyer
33
+        </div>
34
+      </div>
35
+    </div>
36
+  )
37
+}
38
+
39
+export default PersonalData

+ 30 - 0
src/component/Account/UserInfo.jsx View File

@@ -0,0 +1,30 @@
1
+import React from 'react'
2
+
3
+export const UserInfo = props => {
4
+  return (
5
+    <div className='account__userinformation mr-5 ml-5 mb-5'>
6
+      <div className='account__userinformation__avatar'>
7
+        <img src={props.user.avatar} alt='avatar' />
8
+      </div>
9
+      <div className='account__userinformation__wrapper'>
10
+        <div className='account__userinformation__name mb-3'>
11
+          {`${props.user.firstname} ${props.user.lastname}`}
12
+        </div>
13
+        <a href={`mailto:${props.user.email}`} className='account__userinformation__email mb-3'>
14
+          {props.user.email}
15
+        </a>
16
+        <div className='account__userinformation__role mb-3'>
17
+          {props.user.role}
18
+        </div>
19
+        <div className='account__userinformation__job mb-3'>
20
+          {props.user.job}
21
+        </div>
22
+        <a href='http://www.algoo.fr' className='account__userinformation__company'>
23
+          {props.user.company}
24
+        </a>
25
+      </div>
26
+    </div>
27
+  )
28
+}
29
+
30
+export default UserInfo

+ 3 - 3
src/component/Sidebar/WorkspaceListItem.jsx View File

@@ -25,7 +25,7 @@ const WorkspaceListItem = props => {
25 25
         </div>
26 26
       </div>
27 27
 
28
-      <AnimateHeight duration={500} height={props.isOpen ? 'auto' : 0}>
28
+      <AnimateHeight duration={500} height={props.isOpenInSidebar ? 'auto' : 0}>
29 29
         <ul
30 30
           className='sidebar__navigation__workspace__item__submenu'
31 31
           id={`sidebarSubMenu_${props.number}`}
@@ -174,11 +174,11 @@ WorkspaceListItem.propTypes = {
174 174
   name: PropTypes.string.isRequired,
175 175
   onClickTitle: PropTypes.func,
176 176
   onClickAllContent: PropTypes.func,
177
-  isOpen: PropTypes.bool
177
+  isOpenInSidebar: PropTypes.bool
178 178
 }
179 179
 
180 180
 WorkspaceListItem.defaultProps = {
181 181
   onClickTitle: () => {},
182 182
   onClickAllContent: () => {},
183
-  isOpen: false
183
+  isOpenInSidebar: false
184 184
 }

+ 3 - 1
src/component/common/layout/PageWrapper.jsx View File

@@ -8,7 +8,9 @@ const PageWrapper = props => {
8 8
   return (
9 9
     <div className={classnames(props.customeClass, 'pageWrapperGeneric')}>
10 10
       <div className='container-fluid'>
11
-        {props.children}
11
+        <div className='row'>
12
+          {props.children}
13
+        </div>
12 14
       </div>
13 15
     </div>
14 16
   )

+ 102 - 0
src/container/Account.jsx View File

@@ -0,0 +1,102 @@
1
+import React from 'react'
2
+import { connect } from 'react-redux'
3
+import PageWrapper from '../component/common/layout/PageWrapper.jsx'
4
+import PageTitle from '../component/common/layout/PageTitle.jsx'
5
+import PageContent from '../component/common/layout/PageContent.jsx'
6
+import UserInfo from '../component/Account/UserInfo.jsx'
7
+import MenuSubComponent from '../component/Account/MenuSubComponent.jsx'
8
+import PersonalData from '../component/Account/PersonalData.jsx'
9
+import Calendar from '../component/Account/Calendar.jsx'
10
+import Notification from '../component/Account/Notification.jsx'
11
+import { Delimiter } from 'tracim_lib'
12
+import { updateUserWorkspaceSubscriptionNotif } from '../action-creator.sync.js'
13
+import {
14
+  getTimezone,
15
+  getUserRole
16
+} from '../action-creator.async.js'
17
+
18
+class Account extends React.Component {
19
+  constructor (props) {
20
+    super(props)
21
+
22
+    this.state = {
23
+      subComponentMenu: [{
24
+        name: 'personalData',
25
+        menuLabel: 'Informations Compte',
26
+        active: true
27
+      }, {
28
+        name: 'calendar',
29
+        menuLabel: 'Calendrier',
30
+        active: false
31
+      }, {
32
+        name: 'notification',
33
+        menuLabel: 'Notifications',
34
+        active: false
35
+      }]
36
+    }
37
+  }
38
+
39
+  componentDidMount () {
40
+    const { user, workspaceList, timezone, dispatch } = this.props
41
+
42
+    if (user.id !== -1 && workspaceList.length > 0) dispatch(getUserRole(user))
43
+    if (timezone.length === 0) dispatch(getTimezone())
44
+  }
45
+
46
+  componentDidUpdate () {
47
+    const { user, workspaceList, dispatch } = this.props
48
+    if (user.id !== -1 && workspaceList.length > 0 && workspaceList.some(ws => ws.role === undefined)) dispatch(getUserRole(user))
49
+  }
50
+
51
+  handleClickSubComponentMenuItem = subMenuItemName => this.setState(prev => ({
52
+    subComponentMenu: prev.subComponentMenu.map(m => ({...m, active: m.name === subMenuItemName}))
53
+  }))
54
+
55
+  handleChangeSubscriptionNotif = (workspaceId, subscriptionNotif) =>
56
+    this.props.dispatch(updateUserWorkspaceSubscriptionNotif(workspaceId, subscriptionNotif))
57
+
58
+  render () {
59
+    const subComponent = (() => {
60
+      switch (this.state.subComponentMenu.find(({active}) => active).name) {
61
+        case 'personalData':
62
+          return <PersonalData />
63
+
64
+        case 'calendar':
65
+          return <Calendar user={this.props.user} timezone={this.props.timezone} />
66
+
67
+        case 'notification':
68
+          return <Notification
69
+            workspaceList={this.props.workspaceList}
70
+            onChangeSubscriptionNotif={this.handleChangeSubscriptionNotif}
71
+          />
72
+      }
73
+    })()
74
+
75
+    return (
76
+      <PageWrapper customClass='account'>
77
+        <PageTitle
78
+          parentClass={'account'}
79
+          title={'Mon Compte'}
80
+        />
81
+
82
+        <PageContent parentClass='account'>
83
+          <UserInfo user={this.props.user} />
84
+
85
+          <Delimiter customClass={'account__delimiter'} />
86
+
87
+          <div className='account__userpreference'>
88
+            <MenuSubComponent subMenuList={this.state.subComponentMenu} onClickMenuItem={this.handleClickSubComponentMenuItem} />
89
+
90
+            <div className='account__userpreference__setting'>
91
+              { subComponent }
92
+            </div>
93
+          </div>
94
+
95
+        </PageContent>
96
+      </PageWrapper>
97
+    )
98
+  }
99
+}
100
+
101
+const mapStateToProps = ({ user, workspaceList, timezone }) => ({ user, workspaceList, timezone })
102
+export default connect(mapStateToProps)(Account)

+ 0 - 313
src/container/AccountPage.jsx View File

@@ -1,313 +0,0 @@
1
-import React, { Component } from 'react'
2
-import imgProfil from '../img/imgProfil.png'
3
-import { BtnSwitch } from 'tracim_lib'
4
-
5
-class AccountPage extends Component {
6
-  render () {
7
-    return (
8
-      <div className='account'>
9
-        <div className='container-fluid nopadding'>
10
-          <div className='pageTitleGeneric'>
11
-            <div className='pageTitleGeneric__title'>
12
-              Mon Compte
13
-            </div>
14
-          </div>
15
-
16
-          <div className='account__userinformation mr-5 ml-5 mb-5'>
17
-            <div className='account__userinformation__avatar'>
18
-              <img src={imgProfil} alt='avatar' />
19
-            </div>
20
-            <div className='account__userinformation__wrapper'>
21
-              <div className='account__userinformation__name mb-3'>
22
-                Alexi Cauvin
23
-              </div>
24
-              <a href='mailto:contact@contact.fr' className='account__userinformation__email mb-3'>
25
-                alexi.cauvin@algoo.fr
26
-              </a>
27
-              <div className='account__userinformation__role mb-3'>
28
-                Utilisateur
29
-              </div>
30
-              <div className='account__userinformation__job mb-3'>
31
-                Integrateur | Webdesigner
32
-              </div>
33
-              <a href='www.algoo.fr' className='account__userinformation__company'>
34
-                Algoo
35
-              </a>
36
-            </div>
37
-          </div>
38
-
39
-          <div className='account__delimiter GenericDelimiter' />
40
-
41
-          <div className='account__userpreference'>
42
-
43
-            <nav className='account__userpreference__menu navbar d-flex align-items-start'>
44
-
45
-              <div className='account__userpreference__menu__responsive d-lg-none'>
46
-                <i className='fa fa-bars' />
47
-              </div>
48
-
49
-              <ul className='account__userpreference__menu__list nav flex-column'>
50
-
51
-                <li className='account__userpreference__menu__list__close nav-link'>
52
-                  <i className='fa fa-times' />
53
-                </li>
54
-
55
-                <li className='account__userpreference__menu__list__disabled'>Menu
56
-                </li>
57
-                <li className='account__userpreference__menu__list__item nav-item'>
58
-                  <div className='account__userpreference__menu__list__item__link nav-link'>Informations Compte</div>
59
-                </li>
60
-                <li className='account__userpreference__menu__list__item nav-item'>
61
-                  <div className='account__userpreference__menu__list__item__link nav-link'>Calendrier</div>
62
-                </li>
63
-                <li className='account__userpreference__menu__list__item nav-item'>
64
-                  <div className='account__userpreference__menu__list__item__link nav-link'>Notifications</div>
65
-                </li>
66
-              </ul>
67
-            </nav>
68
-
69
-            <div className='account__userpreference__setting'>
70
-
71
-              <div className='account__userpreference__setting__personaldata d-none'>
72
-                <div className='personaldata__title subTitle ml-2 ml-sm-0'>
73
-                  Mes informations personnelles
74
-                </div>
75
-
76
-                <div className='personaldata__text ml-2 ml-sm-0'>
77
-                  Ut consectetur dolor et sunt nisi officia ut magna. Ut consectetur dolor et sunt nisi officia ut magna.
78
-                  Ut consectetur dolor et sunt nisi officia ut magna.
79
-                </div>
80
-
81
-                <div className='personaldata__form'>
82
-                  <div className='personaldata__form__title'>
83
-                    Changer le mot de passe :
84
-                  </div>
85
-                  <input className='personaldata__form__txtinput form-control' type='password' placeholder='Ancien mot de passe' />
86
-                  <input className='personaldata__form__txtinput form-control' type='password' placeholder='Nouveau mot de passe' />
87
-                  <div className='personaldata__form__button btn btn-primary'>
88
-                    Envoyer
89
-                  </div>
90
-                </div>
91
-
92
-                <div className='personaldata__form'>
93
-                  <div className='personaldata__form__title'>
94
-                    Changer d'adresse mail :
95
-                  </div>
96
-                  <input className='personaldata__form__txtinput form-control' type='email' placeholder='Ancienne adresse mail' />
97
-                  <input className='personaldata__form__txtinput form-control' type='email' placeholder='Nouvelle adresse mail' />
98
-                  <div className='personaldata__form__button btn btn-primary'>
99
-                    Envoyer
100
-                  </div>
101
-                </div>
102
-              </div>
103
-
104
-              <div className='account__userpreference__setting__calendar d-none'>
105
-
106
-                <div className='calendar__title subTitle ml-2 ml-sm-0'>
107
-                  Calendrier
108
-                </div>
109
-
110
-                <div className='calendar__text ml-2 ml-sm-0'>
111
-                  Ut consectetur dolor et sunt nisi officia ut magna. Ut consectetur dolor et sunt nisi officia ut magna.
112
-                  Ut consectetur dolor et sunt nisi officia ut magna.
113
-                </div>
114
-
115
-                <div className='calendar__title ml-2 ml-sm-0'>
116
-                  Accèder à votre Calendrier personnel
117
-                </div>
118
-                <div className='calendar__link ml-2 ml-sm-0'>
119
-                  http://algoo.trac.im/caldav/user/262.ics/
120
-                </div>
121
-
122
-                <div className='calendar__title ml-2 ml-sm-0'>
123
-                  Changer de Fuseau Horaire :
124
-                </div>
125
-
126
-                <div className='calendar__timezone ml-2 ml-sm-0 dropdown'>
127
-                  <button className='calendar__timezone__btn btn dropdown-toggle' type='button' id='dropdownMenuButton' data-toggle='dropdown' aria-haspopup='true' aria-expanded='false'>
128
-                    Fuseau Horaire
129
-                  </button>
130
-                  <div className='calendar__timezone__submenu dropdown-menu'>
131
-                    <div className='calendar__timezone__submenu__item dropdown-item'> Paris GMT +1
132
-                    </div>
133
-                    <div className='calendar__timezone__submenu__item dropdown-item dropdown-item'> Londres GMT +0
134
-                    </div>
135
-                  </div>
136
-                </div>
137
-              </div>
138
-
139
-              <div className='account__userpreference__setting__notification'>
140
-
141
-                <div className='notification__title subTitle ml-2 ml-sm-0'>
142
-                  Mes Espaces de Travail
143
-                </div>
144
-
145
-                <div className='notification__text ml-2 ml-sm-0'>
146
-                  Ut consectetur dolor et sunt nisi officia ut magna. Ut consectetur dolor et sunt nisi officia ut magna.
147
-                  Ut consectetur dolor et sunt nisi officia ut magna.
148
-                </div>
149
-
150
-                <div className='notification__table'>
151
-                  <table className='table'>
152
-                    <thead>
153
-                      <tr>
154
-                        <th>Espace de travail</th>
155
-                        <th>Role</th>
156
-                        <th>Notification</th>
157
-                      </tr>
158
-                    </thead>
159
-                    <tbody>
160
-                      <tr>
161
-                        <td>
162
-                          <div className='notification__table__wksname'>
163
-                            Nouvelle ligne directrice du nouveau design de Tracim v2 en date du 10 Octobre 2017
164
-                          </div>
165
-                        </td>
166
-                        <td>
167
-                          <div className='notification__table__role'>
168
-                            <div className='notification__table__role__icon'>
169
-                              <i className='fa fa-graduation-cap' />
170
-                            </div>
171
-                            <div className='notification__table__role__text d-none d-sm-flex'>
172
-                              Gestionnaire de Contenu
173
-                            </div>
174
-                          </div>
175
-                        </td>
176
-                        <td>
177
-                          <BtnSwitch />
178
-                        </td>
179
-                      </tr>
180
-                      <tr>
181
-                        <td>
182
-                          <div className='notification__table__wksname'>
183
-                            Nouvelle ligne directrice du nouveau design de Tracim v2 en date du 10 Octobre 2017
184
-                          </div>
185
-                        </td>
186
-                        <td>
187
-                          <div className='notification__table__role'>
188
-                            <div className='notification__table__role__icon'>
189
-                              <i className='fa fa-eye' />
190
-                            </div>
191
-                            <div className='notification__table__role__text d-none d-sm-flex'>
192
-                              Lecteur
193
-                            </div>
194
-                          </div>
195
-                        </td>
196
-                        <td>
197
-                          <BtnSwitch />
198
-                        </td>
199
-                      </tr>
200
-                      <tr>
201
-                        <td>
202
-                          <div className='notification__table__wksname'>
203
-                            Nouvelle ligne directrice du nouveau design de Tracim v2 en date du 10 Octobre 2017
204
-                          </div>
205
-                        </td>
206
-                        <td>
207
-                          <div className='notification__table__role'>
208
-                            <div className='notification__table__role__icon'>
209
-                              <i className='fa fa-pencil' />
210
-                            </div>
211
-                            <div className='notification__table__role__text d-none d-sm-flex'>
212
-                              Contributeur
213
-                            </div>
214
-                          </div>
215
-                        </td>
216
-                        <td>
217
-                          <BtnSwitch />
218
-                        </td>
219
-                      </tr>
220
-                      <tr>
221
-                        <td>
222
-                          <div className='notification__table__wksname'>
223
-                            Nouvelle ligne directrice du nouveau design de Tracim v2 en date du 10 Octobre 2017
224
-                          </div>
225
-                        </td>
226
-                        <td>
227
-                          <div className='notification__table__role'>
228
-                            <div className='notification__table__role__icon'>
229
-                              <i className='fa fa-gavel' />
230
-                            </div>
231
-                            <div className='notification__table__role__text d-none d-sm-flex'>
232
-                              Responsable
233
-                            </div>
234
-                          </div>
235
-                        </td>
236
-                        <td>
237
-                          <BtnSwitch />
238
-                        </td>
239
-                      </tr>
240
-                      <tr>
241
-                        <td>
242
-                          <div className='notification__table__wksname'>
243
-                            Nouvelle ligne directrice du nouveau design de Tracim v2 en date du 10 Octobre 2017
244
-                          </div>
245
-                        </td>
246
-                        <td>
247
-                          <div className='notification__table__role'>
248
-                            <div className='notification__table__role__icon'>
249
-                              <i className='fa fa-graduation-cap' />
250
-                            </div>
251
-                            <div className='notification__table__role__text d-none d-sm-flex'>
252
-                              Gestionnaire de Contenu
253
-                            </div>
254
-                          </div>
255
-                        </td>
256
-                        <td>
257
-                          <BtnSwitch />
258
-                        </td>
259
-                      </tr>
260
-                      <tr>
261
-                        <td>
262
-                          <div className='notification__table__wksname'>
263
-                            Nouvelle ligne directrice du nouveau design de Tracim v2 en date du 10 Octobre 2017
264
-                          </div>
265
-                        </td>
266
-                        <td>
267
-                          <div className='notification__table__role'>
268
-                            <div className='notification__table__role__icon'>
269
-                              <i className='fa fa-graduation-cap' />
270
-                            </div>
271
-                            <div className='notification__table__role__text d-none d-sm-flex'>
272
-                              Gestionnaire de Contenu
273
-                            </div>
274
-                          </div>
275
-                        </td>
276
-                        <td>
277
-                          <BtnSwitch />
278
-                        </td>
279
-                      </tr>
280
-                      <tr>
281
-                        <td>
282
-                          <div className='notification__table__wksname'>
283
-                            Nouvelle ligne directrice du nouveau design de Tracim v2 en date du 10 Octobre 2017
284
-                          </div>
285
-                        </td>
286
-                        <td>
287
-                          <div className='notification__table__role'>
288
-                            <div className='notification__table__role__icon'>
289
-                              <i className='fa fa-graduation-cap' />
290
-                            </div>
291
-                            <div className='notification__table__role__text d-none d-sm-flex'>
292
-                              Gestionnaire de Contenu
293
-                            </div>
294
-                          </div>
295
-                        </td>
296
-                        <td>
297
-                          <BtnSwitch />
298
-                        </td>
299
-                      </tr>
300
-                    </tbody>
301
-                  </table>
302
-                </div>
303
-              </div>
304
-
305
-            </div>
306
-          </div>
307
-        </div>
308
-      </div>
309
-    )
310
-  }
311
-}
312
-
313
-export default AccountPage

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

@@ -4,7 +4,7 @@ import { withRouter } from 'react-router'
4 4
 import { translate } from 'react-i18next'
5 5
 import WorkspaceListItem from '../component/Sidebar/WorkspaceListItem.jsx'
6 6
 import { getWorkspaceList } from '../action-creator.async.js'
7
-import { setWorkspaceListIsOpen } from '../action-creator.sync.js'
7
+import { setWorkspaceListisOpenInSidebar } from '../action-creator.sync.js'
8 8
 import { PAGE_NAME } from '../helper.js'
9 9
 
10 10
 class Sidebar extends React.Component {
@@ -17,15 +17,15 @@ class Sidebar extends React.Component {
17 17
 
18 18
   componentDidMount () {
19 19
     const { user, workspaceList, dispatch } = this.props
20
-    user.id !== 0 && workspaceList.length === 0 && dispatch(getWorkspaceList(user.id))
20
+    user.id !== -1 && workspaceList.length === 0 && dispatch(getWorkspaceList(user.id))
21 21
   }
22 22
 
23 23
   componentDidUpdate (prevProps) {
24 24
     const { user, dispatch } = this.props
25
-    user.id !== 0 && prevProps.user.id !== user.id && dispatch(getWorkspaceList(user.id))
25
+    user.id !== -1 && prevProps.user.id !== user.id && dispatch(getWorkspaceList(user.id))
26 26
   }
27 27
 
28
-  handleClickWorkspace = (wsId, newIsOpen) => this.props.dispatch(setWorkspaceListIsOpen(wsId, newIsOpen))
28
+  handleClickWorkspace = (wsId, newisOpenInSidebar) => this.props.dispatch(setWorkspaceListisOpenInSidebar(wsId, newisOpenInSidebar))
29 29
 
30 30
   handleClickAllContent = wsId => {
31 31
     this.props.history.push(`${PAGE_NAME.WS_CONTENT}/${wsId}`)
@@ -43,8 +43,8 @@ class Sidebar extends React.Component {
43 43
                 number={++i}
44 44
                 wsId={ws.id}
45 45
                 name={ws.title}
46
-                isOpen={ws.isOpen}
47
-                onClickTitle={() => this.handleClickWorkspace(ws.id, !ws.isOpen)}
46
+                isOpenInSidebar={ws.isOpenInSidebar}
47
+                onClickTitle={() => this.handleClickWorkspace(ws.id, !ws.isOpenInSidebar)}
48 48
                 onClickAllContent={this.handleClickAllContent}
49 49
                 key={ws.id}
50 50
               />

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

@@ -5,7 +5,7 @@ import Header from './Header.jsx'
5 5
 import Sidebar from './Sidebar.jsx'
6 6
 import Login from './Login.jsx'
7 7
 import Dashboard from './Dashboard.jsx'
8
-import AccountPage from './AccountPage.jsx'
8
+import Account from './Account.jsx'
9 9
 // import FlashMessage from './FlashMessage.jsx'
10 10
 import WorkspaceContent from './WorkspaceContent.jsx'
11 11
 import {
@@ -51,7 +51,7 @@ class Tracim extends React.Component {
51 51
 
52 52
                 <PrivateRoute exact path={PAGE_NAME.HOME} component={WorkspaceContent} />
53 53
                 <PrivateRoute path={`${PAGE_NAME.WS_CONTENT}/:idws`} component={WorkspaceContent} />
54
-                <PrivateRoute exact path={PAGE_NAME.ACCOUNT} component={AccountPage} />
54
+                <PrivateRoute exact path={PAGE_NAME.ACCOUNT} component={Account} />
55 55
                 <PrivateRoute exact path={PAGE_NAME.DASHBOARD} component={Dashboard} />
56 56
 
57 57
               </SidebarWrapper>

+ 2 - 1
src/css/AccountPage.styl View File

@@ -12,7 +12,6 @@ settingText()
12 12
   background-color grey-hover
13 13
 
14 14
 .account
15
-  width 100%
16 15
   &__userinformation
17 16
     display flex
18 17
     justify-content center
@@ -72,6 +71,8 @@ settingText()
72 71
           font-size 18px
73 72
           cursor pointer
74 73
           &:hover
74
+            background-color lightGrey
75
+          &.active
75 76
             background-color blue
76 77
             color white
77 78
     &__setting

+ 3 - 8
src/css/Generic.styl View File

@@ -48,8 +48,8 @@
48 48
   padding 0
49 49
 
50 50
 .pageTitleGeneric
51
-  display flex
52 51
   margin 45px 0
52
+  width 100%
53 53
   &__title
54 54
     margin-left 10px
55 55
     font-size 30px
@@ -60,7 +60,8 @@
60 60
 
61 61
 
62 62
 .pageContentGeneric
63
-  margin 10px 10px 120px 10px
63
+  margin 10px 0 120px 0
64
+  width 100%
64 65
 
65 66
 .dropdownCreateBtn
66 67
   &__label
@@ -115,12 +116,6 @@
115 116
   font-weight 500
116 117
   color blue
117 118
 
118
-.GenericDelimiter
119
-  border-radius 10px
120
-  width 50%
121
-  height 5px
122
-  background-color blue
123
-
124 119
 .btnaction
125 120
   display flex
126 121
   flex-direction column

+ 4 - 0
src/css/Workspace.styl View File

@@ -1,5 +1,9 @@
1 1
 .workspace
2
+  &__header
3
+    display flex
4
+    margin-right 15px
2 5
   &__content
6
+    margin 0 15px
3 7
     &__button
4 8
       display flex
5 9
       justify-content flex-end

+ 22 - 0
src/helper.js View File

@@ -13,3 +13,25 @@ export const PAGE_NAME = {
13 13
   DASHBOARD: '/dashboard',
14 14
   ACCOUNT: '/account'
15 15
 }
16
+
17
+export const ROLE = [{
18
+  id: 0,
19
+  name: 'reader',
20
+  icon: 'fa-eye',
21
+  translationKey: 'role.reader'
22
+}, {
23
+  id: 1,
24
+  name: 'contributor',
25
+  icon: 'fa-pencil',
26
+  translationKey: 'role.contributor'
27
+}, {
28
+  id: 2,
29
+  name: 'content_manager',
30
+  icon: 'fa-graduation-cap',
31
+  translationKey: 'role.content_manager'
32
+}, {
33
+  id: 3,
34
+  name: 'manager',
35
+  icon: 'fa-gavel',
36
+  translationKey: 'role.manager'
37
+}]

+ 2 - 1
src/reducer/root.js View File

@@ -5,7 +5,8 @@ import workspace from './workspace.js'
5 5
 import workspaceList from './workspaceList.js'
6 6
 import activeFileContent from './activeFileContent.js'
7 7
 import app from './app.js'
8
+import timezone from './timezone.js'
8 9
 
9
-const rootReducer = combineReducers({ lang, user, workspace, workspaceList, activeFileContent, app })
10
+const rootReducer = combineReducers({ lang, user, workspace, workspaceList, activeFileContent, app, timezone })
10 11
 
11 12
 export default rootReducer

+ 13 - 0
src/reducer/timezone.js View File

@@ -0,0 +1,13 @@
1
+import { TIMEZONE } from '../action-creator.sync.js'
2
+
3
+export function timezone (state = [], action) {
4
+  switch (action.type) {
5
+    case `Set/${TIMEZONE}`:
6
+      return action.timezone
7
+
8
+    default:
9
+      return state
10
+  }
11
+}
12
+
13
+export default timezone

+ 7 - 3
src/reducer/user.js View File

@@ -10,11 +10,15 @@ const serializeUser = data => ({
10 10
   firstname: data.user.firstname,
11 11
   lastname: data.user.lastname,
12 12
   email: data.user.email,
13
-  avatar: data.user.avatar
13
+  avatar: data.user.avatar,
14
+  role: data.user.role,
15
+  job: data.user.job,
16
+  company: data.user.company,
17
+  caldavUrl: data.user.caldav_url
14 18
 })
15 19
 
16 20
 export default function user (state = {
17
-  id: 0,
21
+  id: -1,
18 22
   isLoggedIn: undefined,
19 23
   username: '',
20 24
   firstname: '',
@@ -23,7 +27,7 @@ export default function user (state = {
23 27
   avatar: ''
24 28
 }, action) {
25 29
   switch (action.type) {
26
-    case `Update/${USER_CONNECTED}`:
30
+    case `Set/${USER_CONNECTED}`:
27 31
       return serializeUser(action.user)
28 32
 
29 33
     case `Update/${USER_DATA}`:

+ 3 - 1
src/reducer/workspace.js View File

@@ -3,7 +3,9 @@ import {
3 3
 } from '../action-creator.sync.js'
4 4
 
5 5
 const serializeWorkspace = data => ({
6
-  ...data,
6
+  id: data.id,
7
+  title: data.title,
8
+  content: data.content,
7 9
   ownerId: data.owner_id
8 10
 })
9 11
 

+ 29 - 5
src/reducer/workspaceList.js View File

@@ -1,18 +1,42 @@
1 1
 import {
2
-  WORKSPACE_LIST
2
+  WORKSPACE_LIST,
3
+  USER_ROLE
3 4
 } from '../action-creator.sync.js'
4 5
 
6
+const serializeWorkspaceItem = data => ({
7
+  id: data.id,
8
+  title: data.title,
9
+  role: data.role,
10
+  notif: data.notif
11
+})
12
+
5 13
 export function workspaceList (state = [], action) {
6 14
   switch (action.type) {
7 15
     case `Update/${WORKSPACE_LIST}`:
8 16
       return action.workspaceList.map(ws => ({
9
-        ...ws,
10
-        isOpen: false
17
+        ...serializeWorkspaceItem(ws),
18
+        isOpenInSidebar: false
11 19
       }))
12 20
 
13
-    case `Set/${WORKSPACE_LIST}/isOpen`:
21
+    case `Set/${WORKSPACE_LIST}/isOpenInSidebar`:
22
+      return state.map(ws => ws.id === action.workspaceId
23
+        ? {...ws, isOpenInSidebar: action.isOpenInSidebar}
24
+        : ws
25
+      )
26
+
27
+    case `Set/${USER_ROLE}`:
28
+      return state.map(ws => {
29
+        const foundWorkspace = action.userRole.find(r => ws.id === r.workspace.id) || {role: '', subscribed_to_notif: ''}
30
+        return {
31
+          ...ws,
32
+          role: foundWorkspace.role,
33
+          notif: foundWorkspace.subscribed_to_notif
34
+        }
35
+      })
36
+
37
+    case `Update/${USER_ROLE}/SubscriptionNotif`:
14 38
       return state.map(ws => ws.id === action.workspaceId
15
-        ? {...ws, isOpen: action.isOpen}
39
+        ? {...ws, notif: action.subscriptionNotif}
16 40
         : ws
17 41
       )
18 42
 

+ 9 - 0
src/translate/en.js View File

@@ -18,6 +18,15 @@ const en = {
18 18
     },
19 19
     Sidebar: {
20 20
       create_new_workspace: 'Create new workspace'
21
+    },
22
+    Account: {
23
+      title: 'My account'
24
+    },
25
+    role: {
26
+      reader: 'Reader',
27
+      contributor: 'Contributor',
28
+      content_manager: 'Content manager',
29
+      manager: 'Manager'
21 30
     }
22 31
   }
23 32
 }

+ 9 - 0
src/translate/fr.js View File

@@ -18,6 +18,15 @@ const fr = {
18 18
     },
19 19
     Sidebar: {
20 20
       create_new_workspace: 'Créer un workspace'
21
+    },
22
+    Account: {
23
+      title: 'Mon compte'
24
+    },
25
+    role: {
26
+      reader: 'Lecteur',
27
+      contributor: 'Contributeur',
28
+      content_manager: 'Gestionnaire de contenu',
29
+      manager: 'Responsable'
21 30
     }
22 31
   }
23 32
 }