Use Route in TeamManager.tsx

This commit is contained in:
Jerko Steiner 2019-03-25 17:19:09 +08:00
parent 7b063c5a09
commit fbeea5ad3e
5 changed files with 164 additions and 162 deletions

View File

@ -4,7 +4,6 @@ import {ITeamState} from './TeamReducer'
import {TeamActions} from './TeamActions' import {TeamActions} from './TeamActions'
import {TeamManager} from './TeamManager' import {TeamManager} from './TeamManager'
import {bindActionCreators} from 'redux' import {bindActionCreators} from 'redux'
import {withRouter} from 'react-router-dom'
export class TeamConnector extends Connector<ITeamState> { export class TeamConnector extends Connector<ITeamState> {
constructor(protected readonly teamActions: TeamActions) { constructor(protected readonly teamActions: TeamActions) {
@ -32,6 +31,6 @@ export class TeamConnector extends Connector<ITeamState> {
TeamManager, TeamManager,
) )
return withRouter(Component) return Component
} }
} }

View File

@ -0,0 +1,88 @@
import React from 'react'
import {Button, Control, Heading, Input} from 'bloomer'
import {ITeam} from '@rondo/common'
import {TeamActions} from './TeamActions'
import {FaPlusSquare, FaCheck, FaEdit} from 'react-icons/fa'
export type ITeamEditorProps = {
type: 'add'
onAddTeam: TeamActions['createTeam']
} | {
type: 'update'
onUpdateTeam: TeamActions['updateTeam']
team: ITeam
}
export interface ITeamEditorState {
name: string
}
export class TeamEditor
extends React.PureComponent<ITeamEditorProps, ITeamEditorState> {
constructor(props: ITeamEditorProps) {
super(props)
this.state = {
name: props.type === 'update' ? this.getName(props.team) : '',
}
}
getName(team?: ITeam) {
return team ? team.name : ''
}
componentWillReceiveProps(nextProps: ITeamEditorProps) {
if (nextProps.type === 'update') {
const {team} = nextProps
if (team !== (this.props as any).team) {
this.setState({
name: this.getName(team),
})
}
}
}
handleChange = (event: React.ChangeEvent<HTMLInputElement>) => {
const name = event.target.value
this.setState({name})
}
handleSubmit = async (event: React.FormEvent) => {
event.preventDefault()
const {name} = this.state
if (this.props.type === 'update') {
const {team} = this.props
await this.props.onUpdateTeam({id: team.id, name})
} else {
await this.props.onAddTeam({name})
}
this.setState({name: ''})
}
render() {
return (
<form
autoComplete='off'
className='team-add'
onSubmit={this.handleSubmit}>
<Heading>
{this.props.type === 'update' ? 'Edit team' : 'Add team'}
</Heading>
<Control hasIcons='left'>
<Input
placeholder='Team Name'
type='text'
value={this.state.name}
onChange={this.handleChange}
/>
<span className='icon is-left'>
{this.props.type === 'update' ? <FaEdit /> : <FaPlusSquare />}
</span>
</Control>
<div className='text-right mt-1'>
<Button
isColor='dark'
className='button'
type='submit'
>
<FaCheck className='mr-1' /> Save
</Button>
</div>
</form>
)
}
}

View File

@ -1,12 +1,10 @@
import React from 'react' import React from 'react'
import {Button, Panel, PanelHeading, PanelBlock} from 'bloomer'
import {FaEdit, FaTimes} from 'react-icons/fa'
import {ITeam, ReadonlyRecord} from '@rondo/common' import {ITeam, ReadonlyRecord} from '@rondo/common'
import {Link} from 'react-router-dom' import {Link} from 'react-router-dom'
import {TeamActions} from './TeamActions' import {TeamActions} from './TeamActions'
import {FaPlusSquare, FaCheck, FaEdit, FaTimes} from 'react-icons/fa' import {TeamEditor} from './TeamEditor'
import {
Button, Control, Heading, Input, Panel, PanelHeading, PanelBlock
} from 'bloomer'
export interface ITeamListProps { export interface ITeamListProps {
teamsById: ReadonlyRecord<number, ITeam>, teamsById: ReadonlyRecord<number, ITeam>,
@ -14,93 +12,14 @@ export interface ITeamListProps {
onAddTeam: TeamActions['createTeam'] onAddTeam: TeamActions['createTeam']
onRemoveTeam: TeamActions['removeTeam'] onRemoveTeam: TeamActions['removeTeam']
onUpdateTeam: TeamActions['updateTeam'] onUpdateTeam: TeamActions['updateTeam']
editTeamId?: number
} }
export interface ITeamProps { export interface ITeamProps {
team: ITeam team: ITeam
editTeamId?: number
onRemoveTeam: TeamActions['removeTeam'] onRemoveTeam: TeamActions['removeTeam']
onUpdateTeam: TeamActions['updateTeam'] onUpdateTeam: TeamActions['updateTeam']
} }
export interface IAddTeamProps {
onAddTeam: TeamActions['createTeam']
onUpdateTeam: TeamActions['updateTeam']
team?: ITeam
}
export interface IAddTeamState {
name: string
}
export class TeamAdd extends React.PureComponent<IAddTeamProps, IAddTeamState> {
constructor(props: IAddTeamProps) {
super(props)
this.state = {
name: this.getName(props.team),
}
}
getName(team?: ITeam) {
return team ? team.name : ''
}
componentWillReceiveProps(nextProps: IAddTeamProps) {
const {team} = nextProps
if (team !== this.props.team) {
this.setState({
name: this.getName(team),
})
}
}
handleChange = (event: React.ChangeEvent<HTMLInputElement>) => {
const name = event.target.value
this.setState({name})
}
handleSubmit = async (event: React.FormEvent) => {
event.preventDefault()
const {team, onAddTeam, onUpdateTeam} = this.props
const {name} = this.state
if (team) {
await onUpdateTeam({id: team.id, name})
} else {
await onAddTeam({name})
}
this.setState({name: ''})
}
render() {
return (
<form
autoComplete='off'
className='team-add'
onSubmit={this.handleSubmit}>
<Heading>
{this.props.team ? 'Edit team' : 'Add team'}
</Heading>
<Control hasIcons='left'>
<Input
placeholder='New Team Name'
type='text'
value={this.state.name}
onChange={this.handleChange}
/>
<span className='icon is-left'>
{this.props.team ? <FaEdit /> : <FaPlusSquare />}
</span>
</Control>
<div className='text-right mt-1'>
<Button
isColor='dark'
className='button'
type='submit'
>
<FaCheck className='mr-1' /> Save
</Button>
</div>
</form>
)
}
}
export class TeamRow extends React.PureComponent<ITeamProps> { export class TeamRow extends React.PureComponent<ITeamProps> {
handleRemove = async () => { handleRemove = async () => {
const {onRemoveTeam, team: {id}} = this.props const {onRemoveTeam, team: {id}} = this.props
@ -115,7 +34,7 @@ export class TeamRow extends React.PureComponent<ITeamProps> {
</div> </div>
<div className='ml-auto'> <div className='ml-auto'>
<Link to={`/teams/${team.id}/users`}> <Link to={`/teams/${team.id}/users`}>
<Button aria-label='Edit'> <Button isInverted isColor='link' aria-label='Edit'>
<FaEdit /> <FaEdit />
</Button> </Button>
</Link> </Link>
@ -124,6 +43,7 @@ export class TeamRow extends React.PureComponent<ITeamProps> {
aria-label='Remove' aria-label='Remove'
onClick={this.handleRemove} onClick={this.handleRemove}
isColor='danger' isColor='danger'
isInverted
> >
<FaTimes /> <FaTimes />
</Button> </Button>
@ -135,45 +55,30 @@ export class TeamRow extends React.PureComponent<ITeamProps> {
export class TeamList extends React.PureComponent<ITeamListProps> { export class TeamList extends React.PureComponent<ITeamListProps> {
render() { render() {
const {editTeamId, teamIds, teamsById} = this.props const {teamIds, teamsById} = this.props
return ( return (
<Panel> <Panel>
{!editTeamId && ( <PanelHeading>Teams</PanelHeading>
<React.Fragment> {teamIds.map(teamId => {
<PanelHeading>Teams</PanelHeading> const team = teamsById[teamId]
{teamIds.map(teamId => { return (
const team = teamsById[teamId] <PanelBlock key={team.id}>
return ( <TeamRow
<PanelBlock key={team.id}> onRemoveTeam={this.props.onRemoveTeam}
<TeamRow onUpdateTeam={this.props.onUpdateTeam}
editTeamId={editTeamId} team={team}
onRemoveTeam={this.props.onRemoveTeam}
onUpdateTeam={this.props.onUpdateTeam}
team={team}
/>
</PanelBlock>
)
})}
<PanelBlock isDisplay='block'>
<TeamAdd
onAddTeam={this.props.onAddTeam}
onUpdateTeam={undefined as any}
/> />
</PanelBlock> </PanelBlock>
</React.Fragment> )
)} })}
{editTeamId && ( <PanelBlock isDisplay='block'>
<PanelBlock isDisplay='block'> <TeamEditor
<TeamAdd type='add'
team={teamsById[editTeamId]} onAddTeam={this.props.onAddTeam}
onAddTeam={undefined as any} />
onUpdateTeam={this.props.onUpdateTeam} </PanelBlock>
/>
</PanelBlock>
)}
</Panel> </Panel>
) )
} }

View File

@ -1,19 +1,13 @@
import React from 'react' import React from 'react'
import {History, Location} from 'history'
import {ITeam, IUserInTeam, ReadonlyRecord} from '@rondo/common' import {ITeam, IUserInTeam, ReadonlyRecord} from '@rondo/common'
import {Route, Switch} from 'react-router-dom'
import {TeamActions} from './TeamActions' import {TeamActions} from './TeamActions'
import {TeamEditor} from './TeamEditor'
import {TeamList} from './TeamList' import {TeamList} from './TeamList'
import {TeamUserList} from './TeamUserList' import {TeamUserList} from './TeamUserList'
import {Title} from 'bloomer' import {Panel, PanelBlock, PanelHeading} from 'bloomer'
import {match} from 'react-router'
export interface ITeamManagerProps { export interface ITeamManagerProps {
history: History
location: Location
match: match<{
teamId: string | undefined
}>
createTeam: TeamActions['createTeam'] createTeam: TeamActions['createTeam']
updateTeam: TeamActions['updateTeam'] updateTeam: TeamActions['updateTeam']
removeTeam: TeamActions['removeTeam'] removeTeam: TeamActions['removeTeam']
@ -36,34 +30,50 @@ export class TeamManager extends React.PureComponent<ITeamManagerProps> {
await this.props.fetchMyTeams() await this.props.fetchMyTeams()
} }
render() { render() {
const {teamId} = this.props.match.params const {teamsById} = this.props
const editTeamId = teamId ? Number(teamId) : undefined
return ( return (
<div className='team-manager'> <div className='team-manager'>
<Title>Teams</Title> <Switch>
<Route exact path='/teams' render={() =>
<TeamList <TeamList
editTeamId={editTeamId} teamsById={teamsById}
teamsById={this.props.teamsById} teamIds={this.props.teamIds}
teamIds={this.props.teamIds} onAddTeam={this.props.createTeam}
onAddTeam={this.props.createTeam} onRemoveTeam={this.props.removeTeam}
onRemoveTeam={this.props.removeTeam} onUpdateTeam={this.props.updateTeam}
onUpdateTeam={this.props.updateTeam} />
/> }/>
<Route exact path='/teams/:teamId/users' render={({match}) => {
{editTeamId && <TeamUserList const {teamId: teamIdParam} = match.params
onAddUser={this.props.addUser} const teamId = teamIdParam ? Number(teamIdParam) : undefined
onRemoveUser={this.props.removeUser} const team = teamId ? teamsById[teamId] : undefined
findUserByEmail={this.props.findUserByEmail} return (
fetchUsersInTeam={this.props.fetchUsersInTeam} <React.Fragment>
<Panel>
teamId={editTeamId} <PanelHeading>Edit Team: {team && team.name}</PanelHeading>
<PanelBlock isDisplay='block'>
userKeysByTeamId={this.props.userKeysByTeamId} {team && <TeamEditor
usersByKey={this.props.usersByKey} type='update'
/>} team={team}
onUpdateTeam={this.props.updateTeam}
/>}
{!team && 'No team loaded'}
</PanelBlock>
</Panel>
{team && <TeamUserList
onAddUser={this.props.addUser}
onRemoveUser={this.props.removeUser}
findUserByEmail={this.props.findUserByEmail}
fetchUsersInTeam={this.props.fetchUsersInTeam}
team={team}
userKeysByTeamId={this.props.userKeysByTeamId}
usersByKey={this.props.usersByKey}
/>}
</React.Fragment>
)
}}/>
</Switch>
</div> </div>
) )
} }

View File

@ -1,5 +1,5 @@
import React from 'react' import React from 'react'
import {IUser, IUserInTeam, ReadonlyRecord} from '@rondo/common' import {ITeam, IUser, IUserInTeam, ReadonlyRecord} from '@rondo/common'
import {TeamActions} from './TeamActions' import {TeamActions} from './TeamActions'
import {FaUser, FaCheck, FaTimes} from 'react-icons/fa' import {FaUser, FaCheck, FaTimes} from 'react-icons/fa'
@ -17,7 +17,7 @@ export interface ITeamUsersProps {
onAddUser: TeamActions['addUser'] onAddUser: TeamActions['addUser']
onRemoveUser: TeamActions['removeUser'] onRemoveUser: TeamActions['removeUser']
teamId: number team: ITeam
userKeysByTeamId: ReadonlyRecord<number, ReadonlyArray<string>> userKeysByTeamId: ReadonlyRecord<number, ReadonlyArray<string>>
usersByKey: ReadonlyRecord<string, IUserInTeam> usersByKey: ReadonlyRecord<string, IUserInTeam>
} }
@ -136,11 +136,11 @@ export class AddUser extends React.PureComponent<IAddUserProps, IAddUserState> {
export class TeamUserList extends React.PureComponent<ITeamUsersProps> { export class TeamUserList extends React.PureComponent<ITeamUsersProps> {
async componentDidMount() { async componentDidMount() {
await this.fetchUsersInTeam(this.props.teamId) await this.fetchUsersInTeam(this.props.team.id)
} async componentWillReceiveProps(nextProps: ITeamUsersProps) { } async componentWillReceiveProps(nextProps: ITeamUsersProps) {
const {teamId} = nextProps const {team} = nextProps
if (teamId !== this.props.teamId) { if (team.id !== this.props.team.id) {
this.fetchUsersInTeam(teamId) this.fetchUsersInTeam(team.id)
} }
} }
async fetchUsersInTeam(teamId: number) { async fetchUsersInTeam(teamId: number) {
@ -149,7 +149,7 @@ export class TeamUserList extends React.PureComponent<ITeamUsersProps> {
} }
} }
render() { render() {
const userKeysByTeamId = this.props.userKeysByTeamId[this.props.teamId] const userKeysByTeamId = this.props.userKeysByTeamId[this.props.team.id]
|| EMPTY_ARRAY || EMPTY_ARRAY
return ( return (
@ -172,7 +172,7 @@ export class TeamUserList extends React.PureComponent<ITeamUsersProps> {
<AddUser <AddUser
onAddUser={this.props.onAddUser} onAddUser={this.props.onAddUser}
onSearchUser={this.props.findUserByEmail} onSearchUser={this.props.findUserByEmail}
teamId={this.props.teamId} teamId={this.props.team.id}
/> />
</PanelBlock> </PanelBlock>
</Panel> </Panel>