gengitkan/client/src/components/BoardPage/BoardPage.tsx

250 lines
7.2 KiB
TypeScript

import React from 'react';
import { Page } from '../Page';
import { connect, DispatchProp } from 'react-redux';
import Board from '@lourenci/react-kanban';
import { fetchBoards } from '../../store/actions/boards';
import { createIssue } from '../../store/actions/issues';
import { buildKanboard, moveCard } from '../../store/actions/kanboards';
import { Loader } from '../Loader';
import { IssueCard } from './IssueCard';
import { Modal } from '../Modal';
export interface BoardPageProps extends DispatchProp {
board: any
kanboard: any
}
export class BoardPage extends React.Component<BoardPageProps> {
state = {
newCardModalActive: false,
newCardLaneID: 0,
newCard: {
title: "",
body: "",
project: ""
}
}
onNewCardTitleChange: (evt: any) => void;
onNewCardBodyChange: (evt: any) => void;
onNewCardProjectChange: (evt: any) => void;
constructor(props: BoardPageProps) {
super(props);
this.renderLaneHeader = this.renderLaneHeader.bind(this);
this.onNewCardClick = this.onNewCardClick.bind(this);
this.onNewCardCloseClick = this.onNewCardCloseClick.bind(this);
this.onNewCardTitleChange = this.onNewCardAttrChange.bind(this, 'title');
this.onNewCardBodyChange = this.onNewCardAttrChange.bind(this, 'body');
this.onNewCardProjectChange = this.onNewCardAttrChange.bind(this, 'project');
this.onNewCardSaveClick = this.onNewCardSaveClick.bind(this);
}
render() {
const { board } = this.props;
return (
<Page title={`${board ? (board.title + ' - ') : ''}GenGitKan`}>
<div className="container is-fluid">
{this.renderBoard()}
</div>
</Page>
);
}
renderBoard() {
const { kanboard } = this.props;
if (!kanboard) {
return <Loader></Loader>
}
return (
<div className="kanboard-container is-fullheight">
<Board disableLaneDrag={true}
renderCard={this.renderCard}
renderLaneHeader={this.renderLaneHeader}
onCardDragEnd={this.onCardDragEnd.bind(this)}>
{kanboard}
</Board>
{ this.renderNewCardModal() }
</div>
);
}
renderNewCardModal() {
const { newCardModalActive, newCardLaneID } = this.state;
const { board } = this.props;
if (!board || !newCardLaneID) return null;
return (
<Modal active={newCardModalActive}>
<div className="new-card-modal">
<article className="message">
<div className="message-header">
<p><span>"{board.lanes[newCardLaneID].title}" - Nouveau ticket</span></p>
<button className="delete" aria-label="delete" onClick={this.onNewCardCloseClick}></button>
</div>
<div className="message-body">
<div className="field">
<div className="control">
<input className="input is-medium" type="text"
placeholder="Titre de votre ticket..."
value={this.state.newCard.title}
onChange={this.onNewCardTitleChange} />
</div>
</div>
<div className="field">
<div className="control">
<textarea className="textarea"
placeholder="Description du nouveau ticket..."
value={this.state.newCard.body}
onChange={this.onNewCardBodyChange}
rows={10}>
</textarea>
</div>
</div>
<div className="field">
<div className="control is-expanded">
<div className="select is-fullwidth">
<select
value={this.state.newCard.project}
onChange={this.onNewCardProjectChange}>
{
board.projects.map((p: any, i: number) => {
return <option key={`new-card-project-${i}`} value={p}>{p}</option>
})
}
</select>
</div>
</div>
</div>
<div className="field is-grouped is-grouped-right">
<p className="control">
<a className="button is-light"
onClick={this.onNewCardCloseClick}>
Annuler
</a>
</p>
<p className="control">
<a className="button is-primary"
onClick={this.onNewCardSaveClick}>
Enregistrer
</a>
</p>
</div>
</div>
</article>
</div>
</Modal>
)
}
renderCard(card: any) {
return <IssueCard card={card} />;
}
renderLaneHeader(lane: any) {
return (
<div className="kanboard-lane">
<div className="level">
<div className="level-left">
<h3 className="level-item is-size-3">{lane.title}</h3>
</div>
<div className="level-right">
<button className="button is-light level-item is-medium"
onClick={this.onNewCardClick.bind(this, lane.id)}>
<span className="icon">
<i className="fas fa-plus" aria-hidden="true"></i>
</span>
</button>
</div>
</div>
</div>
)
}
onCardDragEnd(source: any, dest: any) {
const { board } = this.props;
this.props.dispatch(moveCard(
board.id,
source.fromLaneId,
source.fromPosition,
dest.toLaneId,
dest.toPosition
));
}
componentDidMount() {
const { board } = this.props;
if (!board) {
this.requestBoardsUpdate();
return
}
this.requestBuildKanboard();
}
onNewCardClick(laneID: string) {
const { board } = this.props;
this.setState({
newCardModalActive: true,
newCardLaneID: laneID,
newCard: {
title: "",
body: "",
project: board.projects[0],
}
});
}
onNewCardCloseClick() {
this.setState({ newCardModalActive: false });
}
onNewCardAttrChange(attrName: string, evt: React.ChangeEvent) {
const value = (evt.target as HTMLInputElement).value;
this.setState((state: any) => {
return {
...state,
newCard: {
...state.newCard,
[attrName]: value,
}
}
})
}
onNewCardSaveClick() {
const { newCard, newCardLaneID } = this.state;
const { board } = this.props;
this.setState({ newCardModalActive: false });
this.props.dispatch(createIssue(
newCard.project,
newCard.title,
newCard.body,
board.lanes[newCardLaneID].issueLabel
));
}
componentDidUpdate(prevProps: any) {
if (prevProps.board !== this.props.board) this.requestBuildKanboard();
}
requestBoardsUpdate() {
this.props.dispatch(fetchBoards());
}
requestBuildKanboard() {
const { board } = this.props;
if (!board) return;
this.props.dispatch(buildKanboard(board));
}
}
export const ConnectedBoardPage = connect(function(state: any, props: any) {
const boardID = props.match.params.id;
return {
board: state.boards.byID[boardID],
kanboard: state.kanboards.byID[boardID]
};
})(BoardPage);