Browse Source

integration of account page

Skylsmoi 7 years ago
parent
commit
c86df9953a

+ 5 - 0
jsonserver/server.js View File

1
 const jsonServer = require('json-server')
1
 const jsonServer = require('json-server')
2
 const jsonDb = require('./static_db.json')
2
 const jsonDb = require('./static_db.json')
3
+const timezoneDb = require('./timezone.json')
3
 const server = jsonServer.create()
4
 const server = jsonServer.create()
4
 const router = jsonServer.router() // for persistence : jsonServer.router('static_db.json')
5
 const router = jsonServer.router() // for persistence : jsonServer.router('static_db.json')
5
 const middlewares = jsonServer.defaults()
6
 const middlewares = jsonServer.defaults()
55
   Object.assign({}, jsonDb.workspace_detail, {content: shuffle(jsonDb.workspace_detail.content)})
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
 server.get('/workspace/:idws/content/:idc', (req, res) => {
63
 server.get('/workspace/:idws/content/:idc', (req, res) => {
59
   switch (req.params.idc) {
64
   switch (req.params.idc) {
60
     case '1': // pageHtml
65
     case '1': // pageHtml

+ 48 - 1
jsonserver/static_db.json View File

18
       "firstname": "Côme",
18
       "firstname": "Côme",
19
       "lastname": "Stoilenom",
19
       "lastname": "Stoilenom",
20
       "email": "osef@algoo.fr",
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
   "app_config": [{
28
   "app_config": [{
289
   }, {
293
   }, {
290
     "id": 5,
294
     "id": 5,
291
     "title": "Évolutions"
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
 import { FETCH_CONFIG } from './helper.js'
1
 import { FETCH_CONFIG } from './helper.js'
2
 import {
2
 import {
3
+  TIMEZONE,
4
+  setTimezone,
3
   LANG,
5
   LANG,
4
   updateLangList,
6
   updateLangList,
5
   USER_LOGIN,
7
   USER_LOGIN,
6
   USER_DATA,
8
   USER_DATA,
9
+  USER_ROLE,
7
   USER_CONNECTED,
10
   USER_CONNECTED,
8
-  updateUserConnected,
11
+  setUserConnected,
9
   updateUserData,
12
   updateUserData,
13
+  setUserRole,
10
   WORKSPACE,
14
   WORKSPACE,
11
   updateWorkspaceData,
15
   updateWorkspaceData,
12
   WORKSPACE_LIST,
16
   WORKSPACE_LIST,
71
   if (fetchGetLangList.status === 200) dispatch(updateLangList(fetchGetLangList.json))
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
 export const userLogin = (login, password, rememberMe) => async dispatch => {
88
 export const userLogin = (login, password, rememberMe) => async dispatch => {
75
   const fetchUserLogin = await fetchWrapper({
89
   const fetchUserLogin = await fetchWrapper({
76
     url: `${FETCH_CONFIG.apiUrl}/user/login`,
90
     url: `${FETCH_CONFIG.apiUrl}/user/login`,
86
     actionName: USER_LOGIN,
100
     actionName: USER_LOGIN,
87
     dispatch
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
 export const getIsUserConnected = () => async dispatch => {
106
 export const getIsUserConnected = () => async dispatch => {
96
     actionName: USER_CONNECTED,
110
     actionName: USER_CONNECTED,
97
     dispatch
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
 export const updateUserLang = newLang => async dispatch => { // unused
126
 export const updateUserLang = newLang => async dispatch => { // unused

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

1
+export const TIMEZONE = 'Timezone'
2
+export const setTimezone = timezone => ({ type: `Set/${TIMEZONE}`, timezone })
3
+
1
 export const USER_LOGIN = 'User/Login'
4
 export const USER_LOGIN = 'User/Login'
2
 export const USER_DATA = 'User/Data'
5
 export const USER_DATA = 'User/Data'
3
-
6
+export const USER_ROLE = 'User/Role'
4
 export const USER_CONNECTED = 'User/Connected'
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
 export const updateUserData = userData => ({ type: `Update/${USER_DATA}`, data: userData })
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
 export const WORKSPACE = 'Workspace'
14
 export const WORKSPACE = 'Workspace'
9
 export const updateWorkspaceData = workspace => ({ type: `Update/${WORKSPACE}`, workspace })
15
 export const updateWorkspaceData = workspace => ({ type: `Update/${WORKSPACE}`, workspace })
10
 
16
 
11
 export const WORKSPACE_LIST = 'WorkspaceList'
17
 export const WORKSPACE_LIST = 'WorkspaceList'
12
 export const updateWorkspaceListData = workspaceList => ({ type: `Update/${WORKSPACE_LIST}`, workspaceList })
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
 export const FILE_CONTENT = 'FileContent'
21
 export const FILE_CONTENT = 'FileContent'
16
 export const setActiveFileContentActive = file => ({ type: `Set/${FILE_CONTENT}/Active`, file })
22
 export const setActiveFileContentActive = file => ({ type: `Set/${FILE_CONTENT}/Active`, file })

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

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

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

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

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

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

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

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

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

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
-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
 import { translate } from 'react-i18next'
4
 import { translate } from 'react-i18next'
5
 import WorkspaceListItem from '../component/Sidebar/WorkspaceListItem.jsx'
5
 import WorkspaceListItem from '../component/Sidebar/WorkspaceListItem.jsx'
6
 import { getWorkspaceList } from '../action-creator.async.js'
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
 import { PAGE_NAME } from '../helper.js'
8
 import { PAGE_NAME } from '../helper.js'
9
 
9
 
10
 class Sidebar extends React.Component {
10
 class Sidebar extends React.Component {
17
 
17
 
18
   componentDidMount () {
18
   componentDidMount () {
19
     const { user, workspaceList, dispatch } = this.props
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
   componentDidUpdate (prevProps) {
23
   componentDidUpdate (prevProps) {
24
     const { user, dispatch } = this.props
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
   handleClickAllContent = wsId => {
30
   handleClickAllContent = wsId => {
31
     this.props.history.push(`${PAGE_NAME.WS_CONTENT}/${wsId}`)
31
     this.props.history.push(`${PAGE_NAME.WS_CONTENT}/${wsId}`)
43
                 number={++i}
43
                 number={++i}
44
                 wsId={ws.id}
44
                 wsId={ws.id}
45
                 name={ws.title}
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
                 onClickAllContent={this.handleClickAllContent}
48
                 onClickAllContent={this.handleClickAllContent}
49
                 key={ws.id}
49
                 key={ws.id}
50
               />
50
               />

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

5
 import Sidebar from './Sidebar.jsx'
5
 import Sidebar from './Sidebar.jsx'
6
 import Login from './Login.jsx'
6
 import Login from './Login.jsx'
7
 import Dashboard from './Dashboard.jsx'
7
 import Dashboard from './Dashboard.jsx'
8
-import AccountPage from './AccountPage.jsx'
8
+import Account from './Account.jsx'
9
 // import FlashMessage from './FlashMessage.jsx'
9
 // import FlashMessage from './FlashMessage.jsx'
10
 import WorkspaceContent from './WorkspaceContent.jsx'
10
 import WorkspaceContent from './WorkspaceContent.jsx'
11
 import {
11
 import {
51
 
51
 
52
                 <PrivateRoute exact path={PAGE_NAME.HOME} component={WorkspaceContent} />
52
                 <PrivateRoute exact path={PAGE_NAME.HOME} component={WorkspaceContent} />
53
                 <PrivateRoute path={`${PAGE_NAME.WS_CONTENT}/:idws`} component={WorkspaceContent} />
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
                 <PrivateRoute exact path={PAGE_NAME.DASHBOARD} component={Dashboard} />
55
                 <PrivateRoute exact path={PAGE_NAME.DASHBOARD} component={Dashboard} />
56
 
56
 
57
               </SidebarWrapper>
57
               </SidebarWrapper>

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

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

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

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

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

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

+ 22 - 0
src/helper.js View File

13
   DASHBOARD: '/dashboard',
13
   DASHBOARD: '/dashboard',
14
   ACCOUNT: '/account'
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
 import workspaceList from './workspaceList.js'
5
 import workspaceList from './workspaceList.js'
6
 import activeFileContent from './activeFileContent.js'
6
 import activeFileContent from './activeFileContent.js'
7
 import app from './app.js'
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
 export default rootReducer
12
 export default rootReducer

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

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
   firstname: data.user.firstname,
10
   firstname: data.user.firstname,
11
   lastname: data.user.lastname,
11
   lastname: data.user.lastname,
12
   email: data.user.email,
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
 export default function user (state = {
20
 export default function user (state = {
17
-  id: 0,
21
+  id: -1,
18
   isLoggedIn: undefined,
22
   isLoggedIn: undefined,
19
   username: '',
23
   username: '',
20
   firstname: '',
24
   firstname: '',
23
   avatar: ''
27
   avatar: ''
24
 }, action) {
28
 }, action) {
25
   switch (action.type) {
29
   switch (action.type) {
26
-    case `Update/${USER_CONNECTED}`:
30
+    case `Set/${USER_CONNECTED}`:
27
       return serializeUser(action.user)
31
       return serializeUser(action.user)
28
 
32
 
29
     case `Update/${USER_DATA}`:
33
     case `Update/${USER_DATA}`:

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

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

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

1
 import {
1
 import {
2
-  WORKSPACE_LIST
2
+  WORKSPACE_LIST,
3
+  USER_ROLE
3
 } from '../action-creator.sync.js'
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
 export function workspaceList (state = [], action) {
13
 export function workspaceList (state = [], action) {
6
   switch (action.type) {
14
   switch (action.type) {
7
     case `Update/${WORKSPACE_LIST}`:
15
     case `Update/${WORKSPACE_LIST}`:
8
       return action.workspaceList.map(ws => ({
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
       return state.map(ws => ws.id === action.workspaceId
38
       return state.map(ws => ws.id === action.workspaceId
15
-        ? {...ws, isOpen: action.isOpen}
39
+        ? {...ws, notif: action.subscriptionNotif}
16
         : ws
40
         : ws
17
       )
41
       )
18
 
42
 

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

18
     },
18
     },
19
     Sidebar: {
19
     Sidebar: {
20
       create_new_workspace: 'Create new workspace'
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
     },
18
     },
19
     Sidebar: {
19
     Sidebar: {
20
       create_new_workspace: 'Créer un workspace'
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
 }