1
0
mirror of https://github.com/BoostIo/Boostnote synced 2025-12-13 17:56:25 +00:00

add Member setting, Team setting, FolderSetting(80%)

bump react-select
This commit is contained in:
Rokt33r
2015-10-24 20:52:10 +09:00
parent 3539bd1e79
commit 911cfd8642
15 changed files with 985 additions and 188 deletions

View File

@@ -8,9 +8,9 @@ import Preferences from 'boost/components/modal/Preferences'
import CreateNewFolder from 'boost/components/modal/CreateNewFolder' import CreateNewFolder from 'boost/components/modal/CreateNewFolder'
export default class ArticleNavigator extends React.Component { export default class ArticleNavigator extends React.Component {
// componentDidMount () { componentDidMount () {
// this.handlePreferencesButtonClick() this.handlePreferencesButtonClick()
// } }
handlePreferencesButtonClick (e) { handlePreferencesButtonClick (e) {
openModal(Preferences) openModal(Preferences)
@@ -51,7 +51,7 @@ export default class ArticleNavigator extends React.Component {
return ( return (
<button onClick={e => this.handleFolderButtonClick(folder.name)(e)} key={'folder-' + folder.id} className={isActive ? 'active' : ''}> <button onClick={e => this.handleFolderButtonClick(folder.name)(e)} key={'folder-' + folder.id} className={isActive ? 'active' : ''}>
<FolderMark id={folder.id}/> {folder.name} {folder.public ? <i className='fa fa-fw fa-lock'/> : null}</button> <FolderMark id={folder.id}/> {folder.name} {folder.public ? null : <i className='fa fa-fw fa-lock'/>}</button>
) )
}) })
: [] : []

View File

@@ -15,6 +15,9 @@ articleNavBgColor = #353535
color brandColor color brandColor
font-size 28px font-size 28px
padding 6px 0 0 10px padding 6px 0 0 10px
white-space nowrap
text-overflow ellipsis
overflow-x hidden
.userName .userName
color white color white
padding-left 20px padding-left 20px

View File

@@ -58,6 +58,7 @@ iptFocusBorderColor = #369DCD
absolute right bottom absolute right bottom
top 50px top 50px
left 180px left 180px
overflow-y auto
&>.section &>.section
padding 10px padding 10px
border-bottom 1px solid borderColor border-bottom 1px solid borderColor
@@ -119,8 +120,304 @@ iptFocusBorderColor = #369DCD
&.success &.success
background-color successBackgroundColor background-color successBackgroundColor
color successTextColor color successTextColor
&.AppSettingTab
.description .description
marked() marked()
&.TeamSettingTab
.header
border-bottom 1px solid borderColor
padding 10px
font-size 18px
color brandColor
line-height 33px
.teamSelect
border 1px solid borderColor
height 33px
width 200px
margin 0 10px
outline none
font-size 14px
&:focus
border-color iptFocusBorderColor
.teamDeleteConfirm
label
line-height 33px
font-size 14px
.teamDelete
label
line-height 33px
font-size 18px
color brandColor
.teamDelete, .teamDeleteConfirm
padding 15px 20px 15px 15px
button
background-color white
height 33px
font-size 14px
padding 0 15px
border 1px solid borderColor
float right
margin 0 5px
border-radius 5px
&:hover
background-color darken(white, 10%)
button.deleteBtn
background-color brandColor
border none
color white
&:hover
background-color lighten(brandColor, 10%)
&.MemberSettingTab
&>.header
border-bottom 1px solid borderColor
padding 10px
font-size 18px
color brandColor
line-height 33px
.teamSelect
border 1px solid borderColor
height 33px
width 200px
margin 0 10px
outline none
font-size 14px
&:focus
border-color iptFocusBorderColor
.membersTableSection
.addMember
clearfix()
padding 10px
.addMemberLabel
font-size 14px
line-height 33px
float left
.addMemberControl
width 330px
float left
margin-left 25px
.Select
display block
margin 0
float left
width 280px
height 33px
font-size 14px
border none
line-height 33px
background-color transparent
outline none
&.is-focus
.Select-control
border-color iptFocusBorderColor
.Select-control
height 33px
line-height 33px
padding 0 0 0 15px
border-radius 5px 0 0 5px
border 1px solid borderColor
border-right none
.Select-placeholder
padding 0 0 0 15px
.Seleect-arrow
top 21px
.Select-clear
padding 0 10px
.Select-noresults, .Select-option
line-height 33px
padding 0 0 0 15px
button
font-weight 400
height 33px
cursor pointer
margin 0
padding 0
width 50px
float right
border none
background-color brandColor
border-top-right-radius 5px
border-bottom-right-radius 5px
color white
font-size 14px
.memberList
&>.header
clearfix()
&>.userName
float left
&>.role
float left
&>.control
float right
&>li
border-bottom 1px solid borderColor
height 44px
padding 0 25px
width 420px
margin 0 auto
clearfix()
&:nth-last-child(1)
border-bottom-color transparent
.colUserName
float left
width 250px
clearfix()
.userPhoto
width 30px
height 30px
float left
margin-top 7px
margin-right 15px
border-radius 15px
.userInfo
float left
margin-top 7px
.userName
font-size 16px
margin-bottom 2px
.userEmail
font-size 12px
.colRole
float left
width 75px
.userRole
height 30px
background-color transparent
border 1px solid transparent
margin-top 7px
margin-right 35px
outline none
cursor pointer
&:hover
border-color borderColor
&:focus
border-color iptFocusBorderColor
&:disabled
border-color transparent
cursor not-allowed
.colDelete
width 45px
float right
text-align center
button.deleteButton
border none
height 30px
width 30px
margin-top 7px
background-color transparent
color stripBtnColor
&:hover
color stripHoverBtnColor
&:disabled
color lighten(stripBtnColor, 10%)
cursor not-allowed
&.header
.colRole, .colDelete
text-align center
.colUserName, .colRole, .colDelete
line-height 44px
&.FolderSettingTab
&>.header
border-bottom 1px solid borderColor
padding 10px
font-size 18px
color brandColor
line-height 33px
.teamSelect
border 1px solid borderColor
height 33px
width 200px
margin 0 10px
outline none
font-size 14px
&:focus
border-color iptFocusBorderColor
.section
.folderTable
width 420px
margin 15px auto
&>div
border-bottom 1px solid borderColor
clearfix()
height 43px
line-height 33px
padding 5px 0
&:last-child
border-color transparent
.folderName
float left
width 175px
padding-left 15px
.folderPublic
float left
text-align center
width 100px
.folderControl
float right
width 145px
text-align center
&.FolderRow
.folderName input
height 33px
border 1px solid borderColor
border-radius 5px
padding 0 10px
font-size 14px
outline none
width 150px
&:focus
border-color iptFocusBorderColor
.folderPublic select
height 33px
border 1px solid borderColor
background-color white
outline none
display block
margin 0 auto
&:focus
border-color iptFocusBorderColor
.folderControl
button
border none
height 30px
width 30px
margin-top 1.5px
font-size 14px
background-color transparent
color stripBtnColor
&:hover
color stripHoverBtnColor
&:disabled
color lighten(stripBtnColor, 10%)
cursor not-allowed
&.edit
.folderControl
button
width 60px
&.primary
color brandColor
&:hover
color lighten(brandColor, 10%)
&.delete
.folderDeleteLabel
float left
height 33px
width 250px
padding-left 15px
strong
font-size 16px
color brandColor
.folderControl
button
width 60px
&.primary
color brandColor
&:hover
color lighten(brandColor, 10%)

View File

@@ -86,6 +86,15 @@ export function createTeam (input) {
.send(input) .send(input)
} }
export function updateTeamInfo (teamId, input) {
return request
.put(API_URL + 'teams/' + teamId)
.set({
Authorization: 'Bearer ' + auth.token()
})
.send(input)
}
export function searchUser (key) { export function searchUser (key) {
return request return request
.get(API_URL + 'search/users') .get(API_URL + 'search/users')
@@ -119,6 +128,23 @@ export function createFolder (input) {
.send(input) .send(input)
} }
export function updateFolder (id, input) {
return request
.put(API_URL + 'folders/' + id)
.set({
Authorization: 'Bearer ' + auth.token()
})
.send(input)
}
export function destroyFolder (id) {
return request
.del(API_URL + 'folders/' + id)
.set({
Authorization: 'Bearer ' + auth.token()
})
}
export function sendEmail (input) { export function sendEmail (input) {
return request return request
.post(API_URL + 'mail') .post(API_URL + 'mail')
@@ -139,9 +165,12 @@ export default {
saveArticle, saveArticle,
destroyArticle, destroyArticle,
createTeam, createTeam,
updateTeamInfo,
searchUser, searchUser,
setMember, setMember,
deleteMember, deleteMember,
createFolder, createFolder,
updateFolder,
destroyFolder,
sendEmail sendEmail
} }

View File

@@ -12,6 +12,9 @@ export default class CreateNewFolder extends React.Component {
public: false public: false
} }
} }
handleCloseButton (e) {
this.props.close()
}
handlePublicButtonClick (value) { handlePublicButtonClick (value) {
console.log(value) console.log(value)
@@ -61,7 +64,7 @@ export default class CreateNewFolder extends React.Component {
return ( return (
<div className='CreateNewFolder modal'> <div className='CreateNewFolder modal'>
<button className='closeBtn'><i className='fa fa-fw fa-times'/></button> <button onClick={e => this.handleCloseButton(e)} className='closeBtn'><i className='fa fa-fw fa-times'/></button>
<div className='title'>Create new folder</div> <div className='title'>Create new folder</div>

View File

@@ -140,8 +140,7 @@ export default class CreateNewTeam extends React.Component {
handleMemberDeleteButtonClick (name) { handleMemberDeleteButtonClick (name) {
let selectState = this.state.select let selectState = this.state.select
let input = { let input = {
name: name, name: name
role: 'member'
} }
return e => { return e => {
@@ -175,15 +174,7 @@ export default class CreateNewTeam extends React.Component {
setMember(selectState.team.id, input) setMember(selectState.team.id, input)
.then(res => { .then(res => {
let selectState = this.state.select console.log(res.body)
let team = res.body
team.Members = team.Members.sort((a, b) => {
return new Date(a._pivot_createdAt) - new Date(b._pivot_createdAt)
})
selectState.team = team
selectState.newMember = ''
this.setState({select: selectState})
}) })
.catch(err => { .catch(err => {
if (err.status != null) throw err if (err.status != null) throw err

View File

@@ -0,0 +1,40 @@
import React, { PropTypes } from 'react'
export default class AppSettingTab extends React.Component {
render () {
return (
<div className='AppSettingTab content'>
<div className='section'>
<div className='sectionTitle'>Hotkey</div>
<div className='sectionInput'>
<label>Toggle Finder(popup)</label>
<input type='text'/>
</div>
<div className='sectionConfirm'>
<button>Save</button>
</div>
<div className='description'>
<ul>
<li><code>0</code> to <code>9</code></li>
<li><code>A</code> to <code>Z</code></li>
<li><code>F1</code> to <code>F24</code></li>
<li>Punctuations like <code>~</code>, <code>!</code>, <code>@</code>, <code>#</code>, <code>$</code>, etc.</li>
<li><code>Plus</code></li>
<li><code>Space</code></li>
<li><code>Backspace</code></li>
<li><code>Delete</code></li>
<li><code>Insert</code></li>
<li><code>Return</code> (or <code>Enter</code> as alias)</li>
<li><code>Up</code>, <code>Down</code>, <code>Left</code> and <code>Right</code></li>
<li><code>Home</code> and <code>End</code></li>
<li><code>PageUp</code> and <code>PageDown</code></li>
<li><code>Escape</code> (or <code>Esc</code> for short)</li>
<li><code>VolumeUp</code>, <code>VolumeDown</code> and <code>VolumeMute</code></li>
<li><code>MediaNextTrack</code>, <code>MediaPreviousTrack</code>, <code>MediaStop</code> and <code>MediaPlayPause</code></li>
</ul>
</div>
</div>
</div>
)
}
}

View File

@@ -0,0 +1,121 @@
import React, { PropTypes } from 'react'
import api from 'boost/api'
import linkState from 'boost/linkState'
import FolderMark from 'boost/components/FolderMark'
const IDLE = 'IDLE'
const EDIT = 'EDIT'
const DELETE = 'DELETE'
export default class FolderRow extends React.Component {
constructor (props) {
super(props)
this.state = {
mode: IDLE,
name: props.folder.name,
public: props.folder.public
}
}
handleCancelButtonClick (e) {
this.setState({
mode: IDLE,
name: this.props.folder.name,
public: this.props.folder.public
})
}
handleEditButtonClick (e) {
this.setState({mode: EDIT})
}
handleDeleteButtonClick (e) {
this.setState({mode: DELETE})
}
handleFolderPublicChange (e) {
this.setState({public: e.target.value})
}
handleSaveButtonClick (e) {
let input = {
name: this.state.name,
public: !!parseInt(this.state.public, 10)
}
api.updateFolder(this.props.folder.id, input)
.then(res => {
console.log(res.body)
this.setState({mode: IDLE})
})
.catch(err => {
if (err.status != null) throw err
else console.error(err)
})
}
handleDeleteConfirmButtonClick (e) {
api.destroyFolder(this.props.folder.id)
.then(res => {
console.log(res.body)
})
.catch(err => {
if (err.status != null) throw err
else console.error(err)
})
}
render () {
let folder = this.props.folder
switch (this.state.mode) {
case EDIT:
return (
<div className='FolderRow edit'>
<div className='folderName'>
<input valueLink={this.linkState('name')} type='text'/>
</div>
<div className='folderPublic'>
<select value={this.state.public} onChange={e => this.handleFolderPublicChange(e)}>
<option value='0'>Private</option>
<option value='1'>Public</option>
</select>
</div>
<div className='folderControl'>
<button onClick={e => this.handleSaveButtonClick(e)} className='primary'>Save</button>
<button onClick={e => this.handleCancelButtonClick(e)}>Cancel</button>
</div>
</div>
)
case DELETE:
return (
<div className='FolderRow delete'>
<div className='folderDeleteLabel'>Are you sure to delete <strong>{folder.name}</strong> folder?</div>
<div className='folderControl'>
<button onClick={e => this.handleDeleteConfirmButtonClick(e)} className='primary'>Sure</button>
<button onClick={e => this.handleCancelButtonClick(e)}>Cancel</button>
</div>
</div>
)
case IDLE:
default:
return (
<div className='FolderRow'>
<div className='folderName'><FolderMark id={folder.id}/> {folder.name}</div>
<div className='folderPublic'>{folder.public ? 'Public' : 'Private'}</div>
<div className='folderControl'>
<button onClick={e => this.handleEditButtonClick(e)}><i className='fa fa-fw fa-edit'/></button>
<button onClick={e => this.handleDeleteButtonClick(e)}><i className='fa fa-fw fa-close'/></button>
</div>
</div>
)
}
}
}
FolderRow.propTypes = {
folder: PropTypes.shape()
}
FolderRow.prototype.linkState = linkState

View File

@@ -0,0 +1,63 @@
import React, { PropTypes } from 'react'
import _ from 'lodash'
import FolderRow from './FolderRow'
export default class FolderSettingTab extends React.Component {
getCurrentTeam (props) {
if (props == null) props = this.props
return _.findWhere(props.teams, {id: props.currentTeamId})
}
handleTeamSelectChange (e) {
this.props.switchTeam(e.target.value)
}
renderTeamOptions () {
return this.props.teams.map(team => {
return (
<option key={'team-' + team.id} value={team.id}>{team.name}</option>)
})
}
render () {
let team = this.getCurrentTeam()
console.log(team.Folders)
let folderElements = team.Folders.map(folder => {
return (
<FolderRow key={'folder-' + folder.id} folder={folder}/>
)
})
return (
<div className='FolderSettingTab content'>
<div className='header'>
<span>Setting of</span>
<select
value={this.props.currentTeamId}
onChange={e => this.handleTeamSelectChange(e)}
className='teamSelect'>
{this.renderTeamOptions()}
</select>
</div>
<div className='section'>
<div className='sectionTitle'>Folders</div>
<div className='folderTable'>
<div className='folderHeader'>
<div className='folderName'>Folder name</div>
<div className='folderPublic'>Public/Private</div>
<div className='folderControl'>Edit/Delete</div>
</div>
{folderElements}
</div>
</div>
</div>
)
}
}
FolderSettingTab.propTypes = {
currentTeamId: PropTypes.number,
teams: PropTypes.array,
switchTeam: PropTypes.func
}

View File

@@ -0,0 +1,11 @@
import React, { PropTypes } from 'react'
export default class HelpTab extends React.Component {
render () {
return (
<div className='content help'>
Comming soon
</div>
)
}
}

View File

@@ -0,0 +1,206 @@
import React, { PropTypes } from 'react'
import ProfileImage from 'boost/components/ProfileImage'
import Select from 'react-select'
import api from 'boost/api'
import _ from 'lodash'
function getUsers (input, cb) {
api.searchUser(input)
.then(function (res) {
let users = res.body
cb(null, {
options: users.map(user => {
return { value: user.name, label: user.name }
}),
complete: false
})
})
.catch(function (err) {
console.error(err)
})
}
export default class MemberSettingTab extends React.Component {
constructor (props) {
super(props)
this.state = {
newMember: ''
}
}
getCurrentTeam (props) {
if (props == null) props = this.props
return _.findWhere(props.teams, {id: props.currentTeamId})
}
handleTeamSelectChange (e) {
this.props.switchTeam(e.target.value)
}
handleNewMemberChange (value) {
this.setState({newMember: value})
}
handleClickAddMemberButton (e) {
let team = this.getCurrentTeam()
if (team == null || team.userType !== 'team') return null
let input = {
name: this.state.newMember,
role: 'member'
}
api.setMember(team.id, input)
.then(res => {
console.log(res.body)
})
.catch(err => {
if (err.status != null) throw err
else console.error(err)
})
}
handleMemberRoleChange (name) {
return e => {
let team = this.getCurrentTeam()
let input = {
name: name,
role: e.target.value
}
api.setMember(team.id, input)
.then(res => {
console.log(res.body)
})
.catch(err => {
if (err.status != null) throw err
else console.error(err)
})
}
}
handleMemberDeleteButtonClick (name) {
return e => {
let team = this.getCurrentTeam()
let input = {
name: name
}
api.deleteMember(team.id, input)
.then(res => {
console.log(res.body)
})
.catch(err => {
if (err.status != null) throw err
else console.error(err)
})
}
}
renderTeamOptions () {
return this.props.teams.map(team => {
return (
<option key={'team-' + team.id} value={team.id}>{team.name}</option>)
})
}
render () {
console.log(this.props.teams)
let team = this.getCurrentTeam()
if (team == null || team.userType === 'person') {
return this.renderNoTeam()
}
let membersEl = team.Members.map(member => {
let isCurrentUser = this.props.currentUser.id === member.id
return (
<li key={'user-' + member.id}>
<div className='colUserName'>
<ProfileImage className='userPhoto' email={member.email} size='30'/>
<div className='userInfo'>
<div className='userName'>{`${member.profileName} (${member.name})`}</div>
<div className='userEmail'>{member.email}</div>
</div>
</div>
<div className='colRole'>
<select onChange={e => this.handleMemberRoleChange(member.name)(e)} disabled={isCurrentUser} value={member._pivot_role} className='userRole'>
<option value='owner'>Owner</option>
<option value='member'>Member</option>
</select>
</div>
<div className='colDelete'>
<button className='deleteButton' onClick={e => this.handleMemberDeleteButtonClick(member.name)(e)} disabled={isCurrentUser}><i className='fa fa-times fa-fw'/></button>
</div>
</li>
)
})
return (
<div className='MemberSettingTab content'>
<div className='header'>
<span>Setting of</span>
<select
value={this.props.currentTeamId}
onChange={e => this.handleTeamSelectChange(e)}
className='teamSelect'>
{this.renderTeamOptions()}
</select>
</div>
<div className='membersTableSection section'>
<div className='sectionTitle'>Members</div>
<div className='addMember'>
<div className='addMemberLabel'>Add member</div>
<div className='addMemberControl'>
<Select
className='memberName'
placeholder='Input username to add'
autoload={false}
asyncOptions={getUsers}
onChange={val => this.handleNewMemberChange(val)}
value={this.state.newMember}
/>
<button onClick={e => this.handleClickAddMemberButton(e)} className='addMemberBtn'>add</button>
</div>
</div>
<ul className='memberList'>
<li className='header'>
<div className='colUserName'>Username</div>
<div className='colRole'>Role</div>
<div className='colDelete'>Delete</div>
</li>
{membersEl}
</ul>
</div>
</div>
)
}
renderNoTeam () {
return (
<div className='TeamSettingTab content'>
<div className='header'>
<span>Setting of</span>
<select
value={this.props.currentTeamId}
onChange={e => this.handleTeamSelectChange(e)}
className='teamSelect'>
{this.renderTeamOptions()}
</select>
</div>
<div className='section'>Please select a team</div>
</div>
)
}
}
MemberSettingTab.propTypes = {
currentUser: PropTypes.shape(),
teams: PropTypes.array,
currentTeamId: PropTypes.number,
switchTeam: PropTypes.func
}

View File

@@ -0,0 +1,155 @@
import React, { PropTypes } from 'react'
import _ from 'lodash'
import linkState from 'boost/linkState'
import api from 'boost/api'
export default class TeamSettingTab extends React.Component {
constructor (props) {
super(props)
let team = this.getCurrentTeam(props)
this.state = {
teamName: team != null ? team.profileName : '',
deleteConfirm: false,
alert: null
}
}
componentWillReceiveProps (nextProps) {
let team = this.getCurrentTeam(nextProps)
this.setState({
teamName: team != null ? team.profileName : '',
deleteConfirm: false
})
}
getCurrentTeam (props) {
if (props == null) props = this.props
return _.findWhere(props.teams, {id: props.currentTeamId})
}
handleTeamSelectChange (e) {
this.props.switchTeam(e.target.value)
}
handleSaveButtonClick (e) {
let input = {
profileName: this.state.teamName
}
let alert = {
type: 'info',
message: 'Sending...'
}
this.setState({alert}, () => {
api.updateTeamInfo(this.props.currentTeamId, input)
.then(res => {
console.log(res.body)
let alert = {
type: 'success',
message: 'Successfully done!'
}
this.setState({alert})
})
.catch(err => {
var message
if (err.status != null) {
message = err.response.body.message
} else if (err.code === 'ECONNREFUSED') {
message = 'Can\'t connect to API server.'
} else throw err
let alert = {
type: 'error',
message: message
}
this.setState({alert})
})
})
}
renderTeamOptions () {
return this.props.teams.map(team => {
return (
<option key={'team-' + team.id} value={team.id}>{team.name}</option>)
})
}
render () {
let team = this.getCurrentTeam()
if (team == null || team.userType === 'person') {
return this.renderNoTeam()
}
return (
<div className='TeamSettingTab content'>
<div className='header'>
<span>Setting of</span>
<select
value={this.props.currentTeamId}
onChange={e => this.handleTeamSelectChange(e)}
className='teamSelect'>
{this.renderTeamOptions()}
</select>
</div>
<div className='section'>
<div className='sectionTitle'>Team profile</div>
<div className='sectionInput'>
<label className='label'>Team Name</label>
<input valueLink={this.linkState('teamName')} type='text'/>
</div>
<div className='sectionConfirm'>
<button onClick={e => this.handleSaveButtonClick(e)}>Save</button>
{this.state.alert != null
? (
<div className={'alert ' + this.state.alert.type}>{this.state.alert.message}</div>
)
: null}
</div>
</div>
{!this.state.deleteConfirm
? (
<div className='section teamDelete'>
<label>Delete this team</label>
<button onClick={e => this.setState({deleteConfirm: true})} className='deleteBtn'><i className='fa fa-fw fa-trash'/> Delete</button>
</div>
)
: (
<div className='section teamDeleteConfirm'>
<label>Are you sure to delete this team?</label>
<button onClick={e => this.setState({deleteConfirm: false})}>Cancel</button>
<button className='deleteBtn'><i className='fa fa-fw fa-check'/> Sure</button>
</div>
)}
</div>
)
}
renderNoTeam () {
return (
<div className='TeamSettingTab content'>
<div className='header'>
<span>Setting of</span>
<select
value={this.props.currentTeamId}
onChange={e => this.handleTeamSelectChange(e)}
className='teamSelect'>
{this.renderTeamOptions()}
</select>
</div>
<div className='section'>Please select a team</div>
</div>
)
}
}
TeamSettingTab.propTypes = {
currentTeamId: PropTypes.number,
teams: PropTypes.array,
switchTeam: PropTypes.func
}
TeamSettingTab.prototype.linkState = linkState

View File

@@ -1,43 +1,31 @@
import React, { PropTypes } from 'react' import React, { PropTypes } from 'react'
import { connect, Provider } from 'react-redux' import { connect, Provider } from 'react-redux'
import auth from 'boost/auth'
import linkState from 'boost/linkState' import linkState from 'boost/linkState'
import Select from 'react-select'
import api from 'boost/api' import api from 'boost/api'
import ProfileImage from 'boost/components/ProfileImage'
import store from 'boost/store' import store from 'boost/store'
import AppSettingTab from './Preference/AppSettingTab'
import HelpTab from './Preference/HelpTab'
import TeamSettingTab from './Preference/TeamSettingTab'
import MemberSettingTab from './Preference/MemberSettingTab'
import FolderSettingTab from './Preference/FolderSettingTab'
import { closeModal } from 'boost/modal'
var { findDOMNode } = require('react-dom') var { findDOMNode } = require('react-dom')
const PROFILE = 'PROFILE' const PROFILE = 'PROFILE'
const PREFERENCES = 'PREFERENCES' const APP = 'APP'
const HELP = 'HELP' const HELP = 'HELP'
const TEAM = 'TEAM' const TEAM = 'TEAM'
const MEMBER = 'MEMBER' const MEMBER = 'MEMBER'
const FOLDER = 'FOLDER' const FOLDER = 'FOLDER'
function getUsers (input, cb) {
api.searchUser(input)
.then(function (res) {
let users = res.body
cb(null, {
options: users.map(user => {
return { value: user.name, label: user.name }
}),
complete: false
})
})
.catch(function (err) {
console.error(err)
})
}
class Preferences extends React.Component { class Preferences extends React.Component {
constructor (props) { constructor (props) {
super(props) super(props)
this.state = { this.state = {
currentTab: PROFILE, currentTab: PROFILE,
currentTeamId: props.status.userId,
profile: { profile: {
userInfo: { userInfo: {
profileName: props.currentUser.profileName, profileName: props.currentUser.profileName,
@@ -54,6 +42,10 @@ class Preferences extends React.Component {
} }
} }
switchTeam (teamId) {
this.setState({currentTeamId: teamId})
}
handleNavButtonClick (tab) { handleNavButtonClick (tab) {
return e => { return e => {
this.setState({currentTab: tab}) this.setState({currentTab: tab})
@@ -65,7 +57,7 @@ class Preferences extends React.Component {
let tabs = [ let tabs = [
{target: PROFILE, label: 'Profile'}, {target: PROFILE, label: 'Profile'},
{target: PREFERENCES, label: 'Preferences'}, {target: APP, label: 'Preferences'},
{target: HELP, label: 'Help & Feedback'}, {target: HELP, label: 'Help & Feedback'},
{target: TEAM, label: 'Team setting'}, {target: TEAM, label: 'Team setting'},
{target: MEMBER, label: 'Manage member'}, {target: MEMBER, label: 'Manage member'},
@@ -80,7 +72,7 @@ class Preferences extends React.Component {
<div className='Preferences modal'> <div className='Preferences modal'>
<div className='header'> <div className='header'>
<div className='title'>Setting</div> <div className='title'>Setting</div>
<button onClick={e => this.props.close()} className='closeBtn'>Done</button> <button onClick={e => closeModal()} className='closeBtn'>Done</button>
</div> </div>
<div className='nav'> <div className='nav'>
@@ -93,17 +85,39 @@ class Preferences extends React.Component {
} }
renderContent () { renderContent () {
let currentTeamId = parseInt(this.state.currentTeamId, 10)
let teams = [this.props.currentUser].concat(this.props.currentUser.Teams)
switch (this.state.currentTab) { switch (this.state.currentTab) {
case PREFERENCES: case APP:
return this.renderPreferences() return (<AppSettingTab/>)
case HELP: case HELP:
return this.renderHelp() return (<HelpTab/>)
case TEAM: case TEAM:
return this.renderTeamSetting() return (
<TeamSettingTab
currentTeamId={currentTeamId}
teams={teams}
switchTeam={teamId => this.switchTeam(teamId)}
/>
)
case MEMBER: case MEMBER:
return this.renderMemberSetting() return (
<MemberSettingTab
currentUser={this.props.currentUser}
currentTeamId={currentTeamId}
teams={teams}
switchTeam={teamId => this.switchTeam(teamId)}
/>
)
case FOLDER: case FOLDER:
return this.renderFolderSetting() return (
<FolderSettingTab
currentTeamId={currentTeamId}
teams={teams}
switchTeam={teamId => this.switchTeam(teamId)}
/>
)
case PROFILE: case PROFILE:
default: default:
return this.renderProfile() return this.renderProfile()
@@ -250,7 +264,7 @@ class Preferences extends React.Component {
<div className='sectionConfirm'> <div className='sectionConfirm'>
<button onClick={e => this.handlePasswordSaveButton(e)}>Save</button> <button onClick={e => this.handlePasswordSaveButton(e)}>Save</button>
{this.state.profile.password.alert != null {profileState.password.alert != null
? ( ? (
<div className={'alert ' + profileState.password.alert.type}>{profileState.password.alert.message}</div> <div className={'alert ' + profileState.password.alert.type}>{profileState.password.alert.message}</div>
) )
@@ -261,144 +275,6 @@ class Preferences extends React.Component {
) )
} }
renderPreferences () {
return (
<div className='content preferences'>
<div className='section passwordSection'>
<div className='sectionTitle'>Hotkey</div>
<div className='sectionInput'>
<label>Toggle Finder(popup)</label>
<input type='text'/>
</div>
<div className='sectionConfirm'>
<button>Save</button>
</div>
<div className='description'>
<ul>
<li><code>0</code> to <code>9</code></li>
<li><code>A</code> to <code>Z</code></li>
<li><code>F1</code> to <code>F24</code></li>
<li>Punctuations like <code>~</code>, <code>!</code>, <code>@</code>, <code>#</code>, <code>$</code>, etc.</li>
<li><code>Plus</code></li>
<li><code>Space</code></li>
<li><code>Backspace</code></li>
<li><code>Delete</code></li>
<li><code>Insert</code></li>
<li><code>Return</code> (or <code>Enter</code> as alias)</li>
<li><code>Up</code>, <code>Down</code>, <code>Left</code> and <code>Right</code></li>
<li><code>Home</code> and <code>End</code></li>
<li><code>PageUp</code> and <code>PageDown</code></li>
<li><code>Escape</code> (or <code>Esc</code> for short)</li>
<li><code>VolumeUp</code>, <code>VolumeDown</code> and <code>VolumeMute</code></li>
<li><code>MediaNextTrack</code>, <code>MediaPreviousTrack</code>, <code>MediaStop</code> and <code>MediaPlayPause</code></li>
</ul>
</div>
</div>
</div>
)
}
renderHelp () {
return (
<div className='content help'>
Comming soon
</div>
)
}
renderTeamSetting () {
return (
<div className='content teamSetting'>
<div className='header'>
<select>
<option></option>
</select>
<div>'s Team Setting</div>
</div>
<div className='section'>
<div className='sectionTitle'>Team profile</div>
<div className='sectionInput'>
<div className='label'>Team Name</div>
<input type='text'/>
</div>
<div className='sectionConfirm'>
<button>Save</button>
</div>
</div>
{false
? (
<div className='section teamDelete'>
<div className='label'>Delete this team</div>
<button>Delete</button>
</div>
)
: (
<div className='section teamDeleteConfirm'>
<div>Are you sure to delete this team?</div>
<button>Sure</button>
<button>Cancel</button>
</div>
)}
</div>
)
}
renderMemberSetting () {
let membersEl = [].map(member => {
let isCurrentUser = this.state.currentUser.id === member.id
return (
<li key={'user-' + member.id}>
<ProfileImage className='userPhoto' email={member.email} size='30'/>
<div className='userInfo'>
<div className='userName'>{`${member.profileName} (${member.name})`}</div>
<div className='userEmail'>{member.email}</div>
</div>
<div className='userControl'>
<select onChange={e => this.handleMemberRoleChange(member.name)(e)} disabled={isCurrentUser} value={member._pivot_role} className='userRole'>
<option value='owner'>Owner</option>
<option value='member'>Member</option>
</select>
<button onClick={e => this.handleMemberDeleteButtonClick(member.name)(e)} disabled={isCurrentUser}><i className='fa fa-times fa-fw'/></button>
</div>
</li>
)
})
return (
<div className='content memberSetting'>
<div className='header'>
<select>
<option></option>
</select>
<div>'s Team Setting</div>
</div>
<div>
<Select
className='memberName'
autoload={false}
asyncOptions={getUsers}
onChange={val => this.handleNewMemberChange(val)}
value={null}
/>
<button onClick={e => this.handleClickAddMemberButton(e)} className='addMemberBtn'>add</button>
</div>
<ul className='memberList'>
{membersEl}
</ul>
</div>
)
}
renderFolderSetting () {
return (
<div className='content folderSetting'></div>
)
}
} }
Preferences.propTypes = { Preferences.propTypes = {
@@ -410,9 +286,11 @@ Preferences.prototype.linkState = linkState
function remap (state) { function remap (state) {
let currentUser = state.currentUser let currentUser = state.currentUser
let status = state.status
return { return {
currentUser currentUser,
status
} }
} }

View File

@@ -38,5 +38,5 @@ export function openModal (component, props) {
export function closeModal () { export function closeModal () {
if (modalBase == null) { return } if (modalBase == null) { return }
modalBase.setState({isHidden: true}) modalBase.setState({component: null, componentProps: null, isHidden: true})
} }

View File

@@ -60,7 +60,7 @@
"react-dom": "^0.14.0", "react-dom": "^0.14.0",
"react-redux": "^3.1.0", "react-redux": "^3.1.0",
"react-router": "^1.0.0-rc1", "react-router": "^1.0.0-rc1",
"react-select": "^0.6.10", "react-select": "^0.8.1",
"react-transform-catch-errors": "^1.0.0", "react-transform-catch-errors": "^1.0.0",
"react-transform-hmr": "^1.0.1", "react-transform-hmr": "^1.0.1",
"redbox-react": "^1.1.1", "redbox-react": "^1.1.1",