Use Route in TeamManager.tsx
This commit is contained in:
parent
7b063c5a09
commit
fbeea5ad3e
@ -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
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
88
packages/client/src/team/TeamEditor.tsx
Normal file
88
packages/client/src/team/TeamEditor.tsx
Normal 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>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -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>
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|||||||
@ -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>
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|||||||
@ -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>
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user