Migration du client sur Typescript
This commit is contained in:
429
client/src/components/BoardPage/EditBoardPage.tsx
Normal file
429
client/src/components/BoardPage/EditBoardPage.tsx
Normal file
@ -0,0 +1,429 @@
|
||||
import React from 'react';
|
||||
import { Page } from '../Page';
|
||||
import { connect, DispatchProp } from 'react-redux';
|
||||
import { selectFlagsIsLoading } from '../../store/selectors/flags';
|
||||
import { fetchBoards, saveBoard, deleteBoard } from '../../store/actions/boards';
|
||||
import { fetchProjects } from '../../store/actions/projects';
|
||||
import uuidv4 from 'uuid/v4';
|
||||
import { Loader } from '../Loader';
|
||||
import { RouteComponentProps } from 'react-router';
|
||||
|
||||
export interface EditorBoardPageProps extends DispatchProp, RouteComponentProps {
|
||||
isLoading: boolean
|
||||
projects: any
|
||||
}
|
||||
|
||||
export class EditBoardPage extends React.Component<EditorBoardPageProps> {
|
||||
|
||||
state = {
|
||||
edited: false,
|
||||
board: {
|
||||
id: "",
|
||||
title: "",
|
||||
description: "",
|
||||
projects: [],
|
||||
lanes: []
|
||||
},
|
||||
}
|
||||
|
||||
onBoardTitleChange: (evt: any) => void;
|
||||
onBoardDescriptionChange: (evt: any) => void;
|
||||
onBoardLaneTitleChange: (laneIndex: any, evt: any) => void;
|
||||
onBoardLaneIssueLabelChange: (laneIndex: any, evt: any) => void;
|
||||
|
||||
static getDerivedStateFromProps(props: any, state: any) {
|
||||
const { board, isLoading } = props;
|
||||
|
||||
if (isLoading || !board || state.edited) return state;
|
||||
|
||||
return {
|
||||
edited: false,
|
||||
board: {
|
||||
id: board.id,
|
||||
title: board.title,
|
||||
description: board.description,
|
||||
projects: [ ...board.projects ],
|
||||
lanes: [ ...board.lanes.map((l: any) => ({ ...l })) ]
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
constructor(props: any) {
|
||||
super(props);
|
||||
this.onBoardTitleChange = this.onBoardAttrChange.bind(this, 'title');
|
||||
this.onBoardDescriptionChange = this.onBoardAttrChange.bind(this, 'description');
|
||||
this.onBoardLaneTitleChange = this.onBoardLaneAttrChange.bind(this, 'title');
|
||||
this.onBoardLaneIssueLabelChange = this.onBoardLaneAttrChange.bind(this, 'issueLabel');
|
||||
this.onDeleteBoardClick = this.onDeleteBoardClick.bind(this);
|
||||
}
|
||||
|
||||
render() {
|
||||
const { isLoading } = this.props;
|
||||
const { board } = this.state;
|
||||
|
||||
if (isLoading) {
|
||||
return (
|
||||
<Page>
|
||||
<Loader></Loader>
|
||||
</Page>
|
||||
)
|
||||
};
|
||||
|
||||
return (
|
||||
<Page>
|
||||
<div className="container is-fluid has-margin-top-normal">
|
||||
<div className="columns">
|
||||
<div className="column is-6 is-offset-3">
|
||||
<div className="level is-mobile">
|
||||
<div className="level-left">
|
||||
{
|
||||
board.id ?
|
||||
<h3 className="is-size-3 level-item">Éditer le tableau</h3> :
|
||||
<h3 className="is-size-3 level-item">Nouveau tableau</h3>
|
||||
}
|
||||
</div>
|
||||
<div className="level-right">
|
||||
{
|
||||
board.id ?
|
||||
<button onClick={this.onDeleteBoardClick} className="level-item button is-danger">
|
||||
<span className="icon">
|
||||
<i className="fas fa-trash"></i>
|
||||
</span>
|
||||
<span>Supprimer</span>
|
||||
</button> :
|
||||
null
|
||||
}
|
||||
</div>
|
||||
</div>
|
||||
<div className="field">
|
||||
<label className="label">Titre</label>
|
||||
<div className="control">
|
||||
<input className="input" type="text"
|
||||
value={board.title}
|
||||
onChange={this.onBoardTitleChange} />
|
||||
</div>
|
||||
</div>
|
||||
<div className="field">
|
||||
<label className="label">Description</label>
|
||||
<div className="control">
|
||||
<textarea className="textarea"
|
||||
value={board.description}
|
||||
onChange={this.onBoardDescriptionChange}>
|
||||
</textarea>
|
||||
</div>
|
||||
</div>
|
||||
<hr />
|
||||
{ this.renderProjectSelect() }
|
||||
<hr />
|
||||
{ this.renderLanesSection() }
|
||||
<div className="field is-grouped is-grouped-right">
|
||||
<p className="control">
|
||||
<a className="button is-light is-normal" href="#/">
|
||||
Annuler
|
||||
</a>
|
||||
</p>
|
||||
<p className="control">
|
||||
<a className="button is-success is-normal"
|
||||
onClick={this.onSaveBoardClick.bind(this)}>
|
||||
Enregistrer
|
||||
</a>
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</Page>
|
||||
);
|
||||
}
|
||||
|
||||
renderProjectSelect() {
|
||||
const { projects } = this.props;
|
||||
const { board } = this.state;
|
||||
|
||||
const projectSelectField = (projectIndex: number, value: any, withDeleteAddon: boolean) => {
|
||||
return (
|
||||
<div key={`project-${projectIndex}`} className="field has-addons">
|
||||
<div className="control is-expanded">
|
||||
<div className="select is-fullwidth">
|
||||
<select value={value} onChange={this.onBoardProjectChange.bind(this, projectIndex)}>
|
||||
<option value=""></option>
|
||||
{
|
||||
projects.map((p: any) => {
|
||||
return <option key={`project-${p}`} value={p}>{p}</option>;
|
||||
})
|
||||
}
|
||||
</select>
|
||||
</div>
|
||||
</div>
|
||||
{
|
||||
withDeleteAddon ?
|
||||
<div className="control">
|
||||
<button type="submit" className="button is-danger"
|
||||
onClick={this.onBoardProjectDelete.bind(this, projectIndex)}>
|
||||
<span className="icon">
|
||||
<i className="fas fa-trash"></i>
|
||||
</span>
|
||||
</button>
|
||||
</div> :
|
||||
null
|
||||
}
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
return (
|
||||
<React.Fragment>
|
||||
<label className="label">Projets</label>
|
||||
{
|
||||
board.projects.map((p, i) => {
|
||||
return projectSelectField(i, p, true);
|
||||
})
|
||||
}
|
||||
{ projectSelectField(board.projects.length, '', false) }
|
||||
</React.Fragment>
|
||||
)
|
||||
}
|
||||
|
||||
|
||||
|
||||
renderLanesSection() {
|
||||
|
||||
const { board } = this.state;
|
||||
|
||||
const laneSection = (laneIndex: number, lane: any) => {
|
||||
return (
|
||||
<React.Fragment key={`board-lane-${laneIndex}`}>
|
||||
<div className="columns">
|
||||
<div className="column is-10">
|
||||
<div className="field is-horizontal">
|
||||
<div className="field-label is-normal">
|
||||
<label className="label">Titre</label>
|
||||
</div>
|
||||
<div className="field-body">
|
||||
<div className="field">
|
||||
<div className="control">
|
||||
<input className="input" type="text"
|
||||
value={lane.title}
|
||||
onChange={this.onBoardLaneTitleChange.bind(this, laneIndex)} />
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div className="field is-horizontal">
|
||||
<div className="field-label is-normal">
|
||||
<label className="label">Label associé</label>
|
||||
</div>
|
||||
<div className="field-body">
|
||||
<div className="field">
|
||||
<div className="control">
|
||||
<input className="input" type="text"
|
||||
value={lane.issueLabel}
|
||||
onChange={this.onBoardLaneIssueLabelChange.bind(this, laneIndex)} />
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div className="column is-2 is-flex">
|
||||
<div className="buttons">
|
||||
<button className="button is-danger is-small"
|
||||
onClick={this.onBoardLaneDelete.bind(this, laneIndex)}>
|
||||
<span className="icon">
|
||||
<i className="fas fa-trash"></i>
|
||||
</span>
|
||||
</button>
|
||||
<button className="button is-primary is-small"
|
||||
onClick={this.onBoardLaneMove.bind(this, laneIndex, -1)}>
|
||||
<span className="icon">
|
||||
<i className="fas fa-chevron-up"></i>
|
||||
</span>
|
||||
</button>
|
||||
<button className="button is-primary is-small"
|
||||
onClick={this.onBoardLaneMove.bind(this, laneIndex, 1)}>
|
||||
<span className="icon">
|
||||
<i className="fas fa-chevron-down"></i>
|
||||
</span>
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<hr />
|
||||
</React.Fragment>
|
||||
);
|
||||
};
|
||||
|
||||
const lanes = board.lanes.map((l, i) => laneSection(i, l))
|
||||
|
||||
return (
|
||||
<React.Fragment>
|
||||
<div className="level">
|
||||
<div className="level-left">
|
||||
<label className="label level-item">Voies</label>
|
||||
</div>
|
||||
<div className="level-right">
|
||||
<div className="field is-grouped is-grouped-right level-item">
|
||||
<p className="control">
|
||||
<a className="button is-primary is-small"
|
||||
onClick={this.onBoardLaneAdd.bind(this)}>
|
||||
Ajouter une voie
|
||||
</a>
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
{ lanes }
|
||||
</React.Fragment>
|
||||
)
|
||||
}
|
||||
|
||||
onBoardLaneAdd() {
|
||||
this.setState((state: any) => {
|
||||
const lanes = [
|
||||
...state.board.lanes,
|
||||
{ id: uuidv4(), title: "", issueLabel: "" }
|
||||
];
|
||||
return {
|
||||
...state,
|
||||
edited: true,
|
||||
board: {
|
||||
...state.board,
|
||||
lanes
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
onBoardProjectDelete(projectIndex: number) {
|
||||
this.setState((state: any) => {
|
||||
const projects = [ ...state.board.projects ]
|
||||
projects.splice(projectIndex, 1);
|
||||
return {
|
||||
...state,
|
||||
edited: true,
|
||||
board: {
|
||||
...state.board,
|
||||
projects
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
onBoardLaneMove(laneIndex: number, direction: number) {
|
||||
this.setState((state: any) => {
|
||||
const lanes = [ ...state.board.lanes ];
|
||||
|
||||
const nextLaneIndex = laneIndex+direction;
|
||||
if (nextLaneIndex < 0 || nextLaneIndex >= lanes.length) {
|
||||
return state;
|
||||
}
|
||||
|
||||
const l = lanes[laneIndex];
|
||||
lanes.splice(laneIndex, 1);
|
||||
lanes.splice(nextLaneIndex, 0, l);
|
||||
|
||||
return {
|
||||
...state,
|
||||
edited: true,
|
||||
board: {
|
||||
...state.board,
|
||||
lanes
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
onBoardLaneDelete(laneIndex: number) {
|
||||
this.setState((state: any) => {
|
||||
const lanes = [ ...state.board.lanes ]
|
||||
lanes.splice(laneIndex, 1);
|
||||
return {
|
||||
...state,
|
||||
edited: true,
|
||||
board: {
|
||||
...state.board,
|
||||
lanes
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
onBoardProjectChange(projectIndex: number, evt: React.ChangeEvent) {
|
||||
const value = (evt.target as HTMLInputElement).value ;
|
||||
this.setState((state: any) => {
|
||||
const projects = [ ...state.board.projects ];
|
||||
projects[projectIndex] = value;
|
||||
return {
|
||||
...state,
|
||||
edited: true,
|
||||
board: {
|
||||
...state.board,
|
||||
projects
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
onBoardAttrChange(attrName: string, evt: React.ChangeEvent) {
|
||||
const value = (evt.target as HTMLInputElement).value;
|
||||
this.setState((state: any) => {
|
||||
return {
|
||||
...state,
|
||||
edited: true,
|
||||
board: {
|
||||
...state.board,
|
||||
[attrName]: value,
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
onBoardLaneAttrChange(attrName: string, laneIndex: number, evt: React.ChangeEvent) {
|
||||
const value = (evt.target as HTMLInputElement).value;
|
||||
this.setState((state: any) => {
|
||||
const lanes = [ ...state.board.lanes ];
|
||||
lanes[laneIndex] = {
|
||||
...state.board.lanes[laneIndex],
|
||||
[attrName]: value
|
||||
};
|
||||
return {
|
||||
...state,
|
||||
edited: true,
|
||||
board: {
|
||||
...state.board,
|
||||
lanes
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
onSaveBoardClick() {
|
||||
let { board } = this.state;
|
||||
board = { ...board };
|
||||
if (!board.id) board.id = uuidv4();
|
||||
this.props.dispatch(saveBoard({...board}));
|
||||
this.props.history.push('/');
|
||||
}
|
||||
|
||||
onDeleteBoardClick() {
|
||||
const { board } = this.state;
|
||||
this.props.dispatch(deleteBoard(board.id));
|
||||
this.props.history.push('/');
|
||||
}
|
||||
|
||||
componentDidMount() {
|
||||
this.props.dispatch(fetchBoards());
|
||||
this.props.dispatch(fetchProjects());
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
export const ConnectedEditBoardPage = connect(function(state: any, props: any) {
|
||||
const boardID = props.match.params.id;
|
||||
const board = boardID ? state.boards.byID[boardID] : null;
|
||||
|
||||
const projects = Object.keys(state.projects.byName).sort();
|
||||
|
||||
const isLoading = selectFlagsIsLoading(state, 'FETCH_BOARDS', 'FETCH_PROJECTS');
|
||||
|
||||
return { board, isLoading, projects };
|
||||
})(EditBoardPage);
|
Reference in New Issue
Block a user