1
0
mirror of https://github.com/BoostIo/Boostnote synced 2025-12-14 02:06:29 +00:00

usernavigator done

This commit is contained in:
Rokt33r
2015-10-12 15:52:54 +09:00
parent 1690e6420f
commit a9e12e4384
8 changed files with 214 additions and 46 deletions

View File

@@ -20,7 +20,7 @@ export default class UserNavigator extends Component {
<Link to={'/users/' + user.id} activeClassName='active'> <Link to={'/users/' + user.id} activeClassName='active'>
<ProfileImage email={user.email} size='44'/> <ProfileImage email={user.email} size='44'/>
<div className='userTooltip'>{user.name}</div> <div className='userTooltip'>{user.name}</div>
<div className='keyLabel'>{'⌘' + (index + 1)}</div> {index < 9 ? <div className='keyLabel'>{'⌘' + (index + 1)}</div> : null}
</Link> </Link>
</li> </li>
)) ))

View File

@@ -9,3 +9,27 @@ export function createTeam (input) {
}) })
.send(input) .send(input)
} }
export function searchUser (key) {
return request
.get(apiUrl + 'search/users')
.query({key: key})
}
export function setMember (teamId, input) {
return request
.post(apiUrl + 'teams/' + teamId + '/members')
.set({
Authorization: 'Bearer ' + localStorage.getItem('token')
})
.send(input)
}
export function deleteMember (teamId, input) {
return request
.del(apiUrl + 'teams/' + teamId + '/members')
.set({
Authorization: 'Bearer ' + localStorage.getItem('token')
})
.send(input)
}

View File

@@ -1,7 +1,25 @@
import React, { PropTypes } from 'react' import React, { PropTypes } from 'react'
import ProfileImage from '../../../components/ProfileImage' import ProfileImage from '../../../components/ProfileImage'
import { createTeam } from '../api' import { searchUser, createTeam, setMember, deleteMember } from '../api'
import linkState from '../../../helpers/linkState' import linkState from '../../../helpers/linkState'
import Select from 'react-select'
function getUsers (input, cb) {
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 CreateNewTeam extends React.Component { export default class CreateNewTeam extends React.Component {
constructor (props) { constructor (props) {
@@ -12,16 +30,12 @@ export default class CreateNewTeam extends React.Component {
alert: null alert: null
}, },
select: { select: {
team: {"name":"test123", team: null,
"profileName":"test123", newMember: null,
"userType":"team",
"updatedAt":"2015-10-11T09:01:01.277Z",
"createdAt":"2015-10-11T09:01:01.277Z",
"id":220,"Members":[{"id":213,"email":"fluke8259@gmail.com","name":"rokt33r","profileName":"Rokt33r","userType":"person","createdAt":"2015-10-05T09:01:30.000Z","updatedAt":"2015-10-05T09:01:30.000Z","_pivot_TeamId":220,"_pivot_MemberId":213,"_pivot_role":"owner"}]
},
alert: null alert: null
}, },
currentTab: 'select' currentTab: 'create',
currentUser: JSON.parse(localStorage.getItem('currentUser'))
} }
} }
@@ -56,13 +70,13 @@ export default class CreateNewTeam extends React.Component {
} }
function onError (err) { function onError (err) {
let errorMessage = err.response != null ? err.response.body.message : err.message let errorMessage = err.response != null ? err.response.body.message : 'Can\'t connect to API server.'
let createState = this.state.create let createState = this.state.create
createState.isSending = false createState.isSending = false
createState.alert = { createState.alert = {
type: 'error', type: 'error',
message: 'Can\'t connect to API server.' message: errorMessage
} }
this.setState({ this.setState({
@@ -91,11 +105,98 @@ export default class CreateNewTeam extends React.Component {
</div> </div>
) )
} }
handleNewMemberChange (value) {
let selectState = this.state.select
selectState.newMember = value
this.setState({select: selectState})
}
handleClickAddMemberButton (e) {
let selectState = this.state.select
let input = {
name: selectState.newMember,
role: 'member'
}
setMember(selectState.team.id, input)
.then(res => {
let selectState = this.state.select
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 => {
if (err.status != null) throw err
else console.error(err)
})
}
handleMemberDeleteButtonClick (name) {
let selectState = this.state.select
let input = {
name: name,
role: 'member'
}
return function (e) {
deleteMember(selectState.team.id, input)
.then(res => {
let selectState = this.state.select
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 => {
console.log(err, err.response)
if (err.status != null) throw err
else console.error(err)
})
}.bind(this)
}
handleMemberRoleChange (name) {
return function (e) {
let selectState = this.state.select
let input = {
name: name,
role: e.target.value
}
setMember(selectState.team.id, input)
.then(res => {
let selectState = this.state.select
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 => {
if (err.status != null) throw err
else console.error(err)
})
}.bind(this)
}
renderSelectTab () { renderSelectTab () {
let selectState = this.state.select let selectState = this.state.select
console.log(selectState)
let membersEl = selectState.team.Members.map(member => { let membersEl = selectState.team.Members.map(member => {
let isCurrentUser = this.state.currentUser.id === member.id
return ( return (
<li key={'user-' + member.id}> <li key={'user-' + member.id}>
<ProfileImage className='userPhoto' email={member.email} size='30'/> <ProfileImage className='userPhoto' email={member.email} size='30'/>
@@ -103,12 +204,13 @@ export default class CreateNewTeam extends React.Component {
<div className='userName'>{`${member.profileName} (${member.name})`}</div> <div className='userName'>{`${member.profileName} (${member.name})`}</div>
<div className='userEmail'>{member.email}</div> <div className='userEmail'>{member.email}</div>
</div> </div>
<div className='userControl'> <div className='userControl'>
<select value={member._pivot_role} className='userRole'> <select onChange={e => this.handleMemberRoleChange(member.name)(e)} disabled={isCurrentUser} value={member._pivot_role} className='userRole'>
<option value='owner'>Owner</option> <option value='owner'>Owner</option>
<option value='member'>Member</option> <option value='member'>Member</option>
</select> </select>
<button><i className='fa fa-times fa-fw'/></button> <button onClick={e => this.handleMemberDeleteButtonClick(member.name)(e)} disabled={isCurrentUser}><i className='fa fa-times fa-fw'/></button>
</div> </div>
</li> </li>
) )
@@ -117,9 +219,15 @@ export default class CreateNewTeam extends React.Component {
return ( return (
<div className='selectTab'> <div className='selectTab'>
<div className='title'>Select member</div> <div className='title'>Select member</div>
<div className='ipt'> <div className='memberForm'>
<input type='text' placeholder='Enter user name or e-mail'/> <Select
<button>add</button> className='memberName'
autoload={false}
asyncOptions={getUsers}
onChange={val => this.handleNewMemberChange(val)}
value={selectState.newMember}
/>
<button onClick={e => this.handleClickAddMemberButton(e)} className='addMemberBtn'>add</button>
</div> </div>
<ul className='memberList'> <ul className='memberList'>
{membersEl} {membersEl}

View File

@@ -1,5 +1,6 @@
bgColor = #E6E6E6 bgColor = #E6E6E6
inputBgColor = white inputBgColor = white
iptFocusBorderColor = #369DCD
refreshBtColor = #B3B3B3 refreshBtColor = #B3B3B3
refreshBtnActiveColor = #3A3A3A refreshBtnActiveColor = #3A3A3A
@@ -22,31 +23,32 @@ infoBtnActiveBgColor = #3A3A3A
margin-top 13.5px margin-top 13.5px
margin-left 15px margin-left 15px
width 350px width 350px
border-radius 22px
background-color inputBgColor
padding 5px 15px padding 5px 15px
transition 0.1s transition 0.1s
font-size 16px font-size 16px
border 1px solid transparent border 1px solid transparent
border-color transparent
&.focus
border-color brandBorderColor
input input
absolute top right absolute top left
left 35px width 350px
width 300px border-radius 16.5px
background-color inputBgColor
border 1px solid transparent
padding-left 35px
outline none outline none
font-size 14px font-size 14px
border none
height 33px height 33px
line-height 33px line-height 33px
background-color transparent z-index 0
&:focus
border-color iptFocusBorderColor
i.fa i.fa
position absolute position absolute
display block display block
top 0 top 0
left 10px left 10px
line-height 33px line-height 33px
z-index 1
pointer-events none
&>.refreshBtn &>.refreshBtn
float left float left
width 33px width 33px

View File

@@ -13,7 +13,12 @@ userAnchorActiveBgColor = white
text-align center text-align center
box-sizing border-box box-sizing border-box
ul.userList ul.userList
margin-top 25px position absolute
top 25px
left 0
right 0
bottom 70px
// overflow-y auto
&>li &>li
a a
display block display block

View File

@@ -19,7 +19,7 @@ stripBtnColor = lighten(stripHoverBtnColor, 35%)
background-color transparent background-color transparent
border none border none
color stripBtnColor color stripBtnColor
&hover &:hover
color stripHoverBtnColor color stripHoverBtnColor
.title .title
font-size 32px font-size 32px
@@ -36,7 +36,7 @@ stripBtnColor = lighten(stripHoverBtnColor, 35%)
border-radius 5px border-radius 5px
border solid 1px borderColor border solid 1px borderColor
outline none outline none
&focus &:focus
border-color iptFocusBorderColor border-color iptFocusBorderColor
.alert .alert
padding 0 15px padding 0 15px
@@ -47,6 +47,8 @@ stripBtnColor = lighten(stripHoverBtnColor, 35%)
border-radius 5px border-radius 5px
color infoTextColor color infoTextColor
background-color infoBackgroundColor background-color infoBackgroundColor
white-space nowrap
overflow-x auto
&.error &.error
color errorTextColor color errorTextColor
background-color errorBackgroundColor background-color errorBackgroundColor
@@ -87,31 +89,56 @@ stripBtnColor = lighten(stripHoverBtnColor, 35%)
.ipt .ipt
margin 105px auto 15px margin 105px auto 15px
.selectTab .selectTab
.ipt .memberForm
display block
margin 25px auto 15px margin 25px auto 15px
width 330px
clearfix() clearfix()
padding 0 padding 0
input font-size 14px
height 44px
line-height 44px
outline none
.Select.memberName
display block display block
margin 0 margin 0
float left float left
width 280px width 280px
height 42px height 44px
padding 0 0 0 15px
font-size 14px font-size 14px
border none border none
line-height 44px line-height 44px
background-color transparent background-color transparent
outline none outline none
&:hover &.is-focus
.Select-control
border-color iptFocusBorderColor
.Select-control
height 44px
line-height 44px
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 44px
padding 0 0 0 15px
&:focus, &.focus
border-color iptFocusBorderColor border-color iptFocusBorderColor
button button
font-weight 400 font-weight 400
height 42px height 44px
cursor pointer cursor pointer
margin 0 margin 0
padding 0 padding 0
width 48px width 50px
float right float right
border none border none
background-color brandColor background-color brandColor
@@ -158,9 +185,9 @@ stripBtnColor = lighten(stripHoverBtnColor, 35%)
margin-right 35px margin-right 35px
outline none outline none
cursor pointer cursor pointer
&hover &:hover
border-color borderColor border-color borderColor
&focus &:focus
border-color iptFocusBorderColor border-color iptFocusBorderColor
button button
border none border none
@@ -168,5 +195,5 @@ stripBtnColor = lighten(stripHoverBtnColor, 35%)
margin-top 7px margin-top 7px
background-color transparent background-color transparent
color stripBtnColor color stripBtnColor
&hover &:hover
color stripHoverBtnColor color stripHoverBtnColor

View File

@@ -141,8 +141,8 @@
cursor: pointer; cursor: pointer;
} }
.Select-menu-outer { .Select-menu-outer {
border-bottom-right-radius: 4px; border-bottom-right-radius: 5px;
border-bottom-left-radius: 4px; border-bottom-left-radius: 5px;
background-color: #ffffff; background-color: #ffffff;
border: 1px solid #cccccc; border: 1px solid #cccccc;
border-top-color: #e6e6e6; border-top-color: #e6e6e6;
@@ -168,8 +168,8 @@
padding: 8px 10px; padding: 8px 10px;
} }
.Select-option:last-child { .Select-option:last-child {
border-bottom-right-radius: 4px; border-bottom-right-radius: 5px;
border-bottom-left-radius: 4px; border-bottom-left-radius: 5px;
} }
.Select-option.is-focused { .Select-option.is-focused {
background-color: #f2f9fc; background-color: #f2f9fc;

View File

@@ -5,6 +5,7 @@ module.exports = {
}, },
output: { output: {
filename: '[name].js', filename: '[name].js',
sourceMapFilename: '[name].map',
publicPath: 'http://localhost:8090/assets', publicPath: 'http://localhost:8090/assets',
libraryTarget: 'commonjs2' libraryTarget: 'commonjs2'
}, },
@@ -41,7 +42,8 @@ module.exports = {
'react-transform-hmr', 'react-transform-hmr',
'react-transform-catch-errors', 'react-transform-catch-errors',
'redux-devtools', 'redux-devtools',
'redux-devtools/lib/react' 'redux-devtools/lib/react',
'react-select'
], ],
resolve: { resolve: {
extensions: ['', '.js', '.jsx', 'styl'] extensions: ['', '.js', '.jsx', 'styl']