ManagedProperties Added to Task

This commit is contained in:
petkir 2020-06-19 14:58:13 +02:00
parent fa41aaad50
commit b53b9a7f5f
11 changed files with 271 additions and 36 deletions

View File

@ -0,0 +1,8 @@
import {IKanbanTask} from './IKanbanTask';
import { IKanbanBucket } from './IKanbanBucket';
export interface IKanbanBoardRenderers{
task?: (task:IKanbanTask) => JSX.Element ;
bucketHeadline?: (bucket:IKanbanBucket) => JSX.Element ;
taskDetail?: (task:IKanbanTask) => JSX.Element ;
}

View File

@ -2,4 +2,5 @@ export interface IKanbanBoardTaskSettings {
showLabels: boolean; showLabels: boolean;
showPriority: boolean; showPriority: boolean;
showAssignedTo: boolean; showAssignedTo: boolean;
showTaskDetailsButton: boolean;
} }

View File

@ -2,5 +2,26 @@ export interface IKanbanTask {
taskId: number | string; taskId: number | string;
title: string; title: string;
isCompleted?: boolean; isCompleted?: boolean;
bucket:string; bucket: string;
mamagedProperties?: IKanbanTaskManagedProps[];
}
export interface IKanbanTaskManagedProps {
name: string;
displayName?: string;
type: KanbanTaskMamagedPropertyType;
value: any;
renderer?: (name: string, value: object, type:KanbanTaskMamagedPropertyType) => JSX.Element;
}
export enum KanbanTaskMamagedPropertyType {
string,
number,
percent,
html,
person,
persons,
complex
} }

View File

@ -20,6 +20,9 @@ export interface IKanbanBucketProps extends IKanbanBucket {
onDragLeave: (event, targetbucket: string) => void; onDragLeave: (event, targetbucket: string) => void;
onDrop: (event, targetbucket: string) => void; onDrop: (event, targetbucket: string) => void;
leavingTaskId?: number | string; leavingTaskId?: number | string;
leavingBucket?: string; leavingBucket?: string;
overBucket?: string; overBucket?: string;

View File

@ -3,8 +3,16 @@ import styles from './KanbanComponent.module.scss';
import { IKanbanTask } from './IKanbanTask'; import { IKanbanTask } from './IKanbanTask';
import { IKanbanBoardTaskSettings } from './IKanbanBoardTaskSettings'; import { IKanbanBoardTaskSettings } from './IKanbanBoardTaskSettings';
import { IKanbanBoardTaskActions } from './IKanbanBoardTaskActions'; import { IKanbanBoardTaskActions } from './IKanbanBoardTaskActions';
import { IKanbanBoardRenderers } from './IKanbanBoardRenderers';
import { IKanbanBucket } from './IKanbanBucket'; import { IKanbanBucket } from './IKanbanBucket';
import KanbanBucket from './KanbanBucket'; import KanbanBucket from './KanbanBucket';
import KanbanTaskManagedProp from './KanbanTaskManagedProp';
import { Dialog, DialogType, DialogFooter } from 'office-ui-fabric-react/lib/Dialog';
import { PrimaryButton, DefaultButton } from 'office-ui-fabric-react/lib/Button';
import { IStackStyles, Stack } from 'office-ui-fabric-react/lib/Stack';
import { CommandBar } from 'office-ui-fabric-react/lib/CommandBar'; import { CommandBar } from 'office-ui-fabric-react/lib/CommandBar';
@ -14,6 +22,7 @@ export interface IKanbanComponentProps {
tasksettings: IKanbanBoardTaskSettings; tasksettings: IKanbanBoardTaskSettings;
taskactions: IKanbanBoardTaskActions; taskactions: IKanbanBoardTaskActions;
showCommandbar?: boolean; showCommandbar?: boolean;
renderers?: IKanbanBoardRenderers;
/* /*
showCommandbarNew: boolean; showCommandbarNew: boolean;
allowDialog: boolean; TODO im mock allowDialog: boolean; TODO im mock
@ -24,6 +33,8 @@ export interface IKanbanComponentState {
leavingTaskId?: number | string; leavingTaskId?: number | string;
leavingBucket?: string; leavingBucket?: string;
overBucket?: string; overBucket?: string;
openDialog: boolean;
openTaskId?: number | string;
} }
export default class KanbanComponent extends React.Component<IKanbanComponentProps, IKanbanComponentState> { export default class KanbanComponent extends React.Component<IKanbanComponentProps, IKanbanComponentState> {
@ -32,6 +43,7 @@ export default class KanbanComponent extends React.Component<IKanbanComponentPro
super(props); super(props);
this.state = { this.state = {
openDialog: false,
leavingTaskId: null, leavingTaskId: null,
leavingBucket: null, leavingBucket: null,
overBucket: null overBucket: null
@ -40,6 +52,7 @@ export default class KanbanComponent extends React.Component<IKanbanComponentPro
public render(): React.ReactElement<IKanbanComponentProps> { public render(): React.ReactElement<IKanbanComponentProps> {
const { buckets, tasks, tasksettings, taskactions, showCommandbar } = this.props; const { buckets, tasks, tasksettings, taskactions, showCommandbar } = this.props;
const { openDialog } = this.state;
const { leavingBucket, leavingTaskId, overBucket } = this.state const { leavingBucket, leavingTaskId, overBucket } = this.state
return ( return (
<div> <div>
@ -60,22 +73,92 @@ export default class KanbanComponent extends React.Component<IKanbanComponentPro
buckettasks={tasks.filter((x) => x.bucket == b.bucket)} buckettasks={tasks.filter((x) => x.bucket == b.bucket)}
tasksettings={tasksettings} tasksettings={tasksettings}
taskactions={taskactions} taskactions={taskactions}
openDetails={(x) => alert(x)} openDetails={this.openDialog.bind(this)}
onDrop={this.onDrop.bind(this)} onDrop={this.onDrop.bind(this)}
onDragLeave={this.onDragLeave.bind(this)} onDragLeave={this.onDragLeave.bind(this)}
onDragOver={this.onDragOver.bind(this)} onDragOver={this.onDragOver.bind(this)}
onDragStart={this.onDragStart.bind(this)} onDragStart={this.onDragStart.bind(this)}
/>) />);
} }
)} )}
</div> </div>
{openDialog && (this.renderDetails())}
</div> </div>
); );
} }
private renderDetails(): JSX.Element {
debugger;
const renderer = this.props.renderers && this.props.renderers.taskDetail ? this.props.renderers.taskDetail : this.internalTaskDetailRenderer;
const tasks = this.props.tasks.filter(t => t.taskId == this.state.openTaskId);
if (tasks.length == 1) {
const task = tasks[0];
return (<Dialog
minWidth={600}
hidden={!this.state.openDialog}
onDismiss={this.closeDialog.bind(this)}
dialogContentProps={{
type: DialogType.largeHeader,
title: task.title,
subText: ''
}}
modalProps={{
isBlocking: false,
styles: { main: { minWidth: 600 } }
}}
>
{renderer(task)}
</Dialog>);
}
// Error Not found or more than one
throw "Error Not found or more than one";
return (<div></div>);
}
private internalTaskDetailRenderer(task: IKanbanTask): JSX.Element {
return (<Stack>
{/* <Stack horizontal horizontalAlign="stretch">
<Stack.Item align="auto" styles={rowStyle1}>
<span>% Complete</span>
</Stack.Item>
<Stack.Item align="stretch" styles={rowStyle2}>
<span>{task.bucket}</span>
</Stack.Item>
</Stack>
<KanbanTaskManagedProp name:'person' ... key={p.name+i} />
*/}
{task.mamagedProperties && (
task.mamagedProperties.map((p, i) => {
return (
<KanbanTaskManagedProp {...p} key={p.name + i} />
);
})
)}
</Stack>
);
}
private closeDialog(ev?: React.MouseEvent<HTMLButtonElement>) {
this.setState({
openDialog: false,
openTaskId: undefined
});
}
private openDialog(taskid: number | string) {
this.setState({
openDialog: true,
openTaskId: taskid
});
}
private onDragLeave(event): void { private onDragLeave(event): void {
console.log('onDragLeave'); console.log('onDragLeave');
/* if (this.bucketRef.current.classList.contains(styles.dragover)) { /* if (this.bucketRef.current.classList.contains(styles.dragover)) {
@ -126,19 +209,19 @@ export default class KanbanComponent extends React.Component<IKanbanComponentPro
}*/ }*/
if (event.dataTransfer.getData("sourcebucket") !== targetbucket) { if (event.dataTransfer.getData("sourcebucket") !== targetbucket) {
const sourcebucket = event.dataTransfer.getData("sourcebucket"); const sourcebucket = event.dataTransfer.getData("sourcebucket");
const source = this.props.buckets.filter(s=>s.bucket == sourcebucket)[0]; const source = this.props.buckets.filter(s => s.bucket == sourcebucket)[0];
const target = this.props.buckets.filter(s=>s.bucket == targetbucket)[0]; const target = this.props.buckets.filter(s => s.bucket == targetbucket)[0];
const taskId = event.dataTransfer.getData("taskId"); const taskId = event.dataTransfer.getData("taskId");
if(this.props.taskactions) { if (this.props.taskactions) {
let allowMove= true; let allowMove = true;
if(this.props.taskactions.allowMove) { if (this.props.taskactions.allowMove) {
allowMove= this.props.taskactions.allowMove(taskId, allowMove = this.props.taskactions.allowMove(taskId,
source, source,
target target
); );
} }
if(allowMove && this.props.taskactions.moved) { if (allowMove && this.props.taskactions.moved) {
this.props.taskactions.moved(taskId,target); this.props.taskactions.moved(taskId, target);
} }
} }
} }

View File

@ -6,11 +6,12 @@ import {IKanbanTask} from './IKanbanTask';
import {IKanbanBoardTaskSettings} from './IKanbanBoardTaskSettings'; import {IKanbanBoardTaskSettings} from './IKanbanBoardTaskSettings';
import {IKanbanBoardTaskActions} from './IKanbanBoardTaskActions'; import {IKanbanBoardTaskActions} from './IKanbanBoardTaskActions';
import classNames from 'classnames'; import classNames from 'classnames';
import { IconNames } from 'office-ui-fabric-react';
export interface IKanbanTaskProps extends IKanbanTask,IKanbanBoardTaskSettings,IKanbanBoardTaskActions { export interface IKanbanTaskProps extends IKanbanTask,IKanbanBoardTaskSettings,IKanbanBoardTaskActions {
openDetails?: (taskId: number | string) => void; openDetails: (taskId: number | string) => void;
onDragStart:(event) => void; onDragStart:(event) => void;
isMoving:boolean; isMoving:boolean;
} }
@ -24,7 +25,7 @@ export default class KanbanTask extends React.Component<IKanbanTaskProps, IKanba
} }
public render(): React.ReactElement<IKanbanTaskProps> { public render(): React.ReactElement<IKanbanTaskProps> {
const { title, showLabels, showPriority, showAssignedTo, isCompleted,isMoving } = this.props; const { title, showLabels, showPriority, showAssignedTo, isCompleted,isMoving,showTaskDetailsButton } = this.props;
const showCompleted = !!this.props.toggleCompleted; const showCompleted = !!this.props.toggleCompleted;
const iconCompleted = { iconName: isCompleted ? 'RadioBtnOn' : 'RadioBtnOff' }; const iconCompleted = { iconName: isCompleted ? 'RadioBtnOn' : 'RadioBtnOff' };
return ( return (
@ -42,6 +43,15 @@ export default class KanbanTask extends React.Component<IKanbanTaskProps, IKanba
/>) />)
} }
<div className={styles.title}>{title}</div> <div className={styles.title}>{title}</div>
{ showTaskDetailsButton && (
<IconButton
iconProps={{iconName:'More'}}
title={strings.OpenDetails}
ariaLabel={strings.OpenDetails}
onClick={this._openDetails.bind(this)}
/>)
}
</div> </div>
<div className={styles.membersAndLabels}> <div className={styles.membersAndLabels}>
{showAssignedTo && (<div className={styles.assignedto}></div>)} {showAssignedTo && (<div className={styles.assignedto}></div>)}
@ -58,5 +68,13 @@ export default class KanbanTask extends React.Component<IKanbanTaskProps, IKanba
} }
} }
private _openDetails():void {
debugger;
if(this.props.openDetails) {
this.props.openDetails(this.props.taskId);
}
}
} }

View File

@ -0,0 +1,6 @@
.rowcol1{
flex-basis: 30%;
}
.rowcol2{
flex-basis: 70%;
}

View File

@ -0,0 +1,66 @@
import * as React from 'react';
import styles from './KanbanTaskManagedProp.module.scss';
import { IKanbanTaskManagedProps, KanbanTaskMamagedPropertyType } from './IKanbanTask';
import { IStackStyles, Stack } from 'office-ui-fabric-react/lib/Stack';
import ReactHtmlParser, { processNodes, convertNodeToElement, htmlparser2 } from 'react-html-parser';
export interface IKanbanTaskManagedPropProps extends IKanbanTaskManagedProps { }
export interface IKanbanTaskManagedPropState { }
export default class KanbanTaskManagedProp extends React.Component<IKanbanTaskManagedPropProps, IKanbanTaskManagedPropState> {
public render(): React.ReactElement<IKanbanTaskManagedPropProps> {
const { displayName, name } = this.props;
return (
<Stack horizontal horizontalAlign="stretch">
<Stack.Item align="auto" className={styles.rowcol1}>
<span>{displayName ? displayName : name}</span>
</Stack.Item>
<Stack.Item align="stretch" className={styles.rowcol2}>
{this.renderValue()}
</Stack.Item>
</Stack>
);
}
private renderValue() {
const { name, type, value } = this.props;
if (this.props.renderer) {
return this.props.renderer(name, value, type);
}
switch (this.props.type) {
case KanbanTaskMamagedPropertyType.string:
return (<span>{value} </span>);
break;
case KanbanTaskMamagedPropertyType.number:
return (<span>{'' + value} </span>);
//TODO maybe Formater
break;
case KanbanTaskMamagedPropertyType.percent:
return (<span>{`${(value as any) * 100}%`} </span>);
//TODO maybe better Formater
break;
case KanbanTaskMamagedPropertyType.html:
return (<span>{ReactHtmlParser(value)}</span>);
//TODO maybe better Formater
break;
case KanbanTaskMamagedPropertyType.person:
return (<span>TODO</span>);
//TODO
break;
case KanbanTaskMamagedPropertyType.persons:
return (<span>TODO</span>);
//TODO
break;
case KanbanTaskMamagedPropertyType.complex:
return (<span>{JSON.stringify(value)}</span>);
break;
default:
throw "Unknow KanbanTaskMamagedPropertyType";
break;
}
}
}

View File

@ -1,7 +1,7 @@
import * as React from 'react'; import * as React from 'react';
import KanbanComponent from './KanbanComponent'; import KanbanComponent from './KanbanComponent';
import { IKanbanBucket } from './IKanbanBucket'; import { IKanbanBucket } from './IKanbanBucket';
import { IKanbanTask } from './IKanbanTask'; import { IKanbanTask, KanbanTaskMamagedPropertyType } from './IKanbanTask';
import { findIndex } from "lodash"; import { findIndex } from "lodash";
export interface IMockKanbanProps { } export interface IMockKanbanProps { }
@ -17,19 +17,45 @@ export class MockKanban extends React.Component<IMockKanbanProps, IMockKanbanSta
super(props); super(props);
this.state = { this.state = {
buckets:[ buckets: [
{bucket:'Not Started', bucketheadline:'Not Started Head',percentageComplete:0, color:'yellow' ,allowAddTask:true}, { bucket: 'Not Started', bucketheadline: 'Not Started Head', percentageComplete: 0, color: 'yellow', allowAddTask: true },
{bucket:'Test1', bucketheadline:'Test1 Head',percentageComplete:10, color:'orange',allowAddTask:true }, { bucket: 'Test1', bucketheadline: 'Test1 Head', percentageComplete: 10, color: 'orange', allowAddTask: true },
{bucket:'Test2', bucketheadline:'Test2 Head',percentageComplete:50, color:'green' }, { bucket: 'Test2', bucketheadline: 'Test2 Head', percentageComplete: 50, color: 'green' },
{bucket:'Test3', bucketheadline:'Test3 Head',percentageComplete:50, color:'#FF0000' }, { bucket: 'Test3', bucketheadline: 'Test3 Head', percentageComplete: 50, color: '#FF0000' },
{bucket:'Test4', bucketheadline:'Test4 Head',percentageComplete:0 ,allowAddTask:true } { bucket: 'Test4', bucketheadline: 'Test4 Head', percentageComplete: 0, allowAddTask: true }
], ],
tasks: [ tasks: [
{taskId: 1, title:'test1',bucket:'Not Started'}, {
{taskId: 2, title:'test2',bucket:'Not Started'}, taskId: 1, title: 'test1', bucket: 'Not Started',
{taskId: 3, title:'test3',bucket:'Not Started'}, mamagedProperties: [
{taskId: '4', title:'test 4',bucket:'Test4'}, {
{taskId: '5', title:'test 5',bucket:'Test3'}, name: 'Prop1',
displayName: 'Prop1 Display',
type: KanbanTaskMamagedPropertyType.html,
value: '<p>test<b>Bold</b></p>'
},
{
name: 'Prop2',
displayName: 'Prop2 Display',
type: KanbanTaskMamagedPropertyType.complex,
value: '<p>test<b>Bold</b></p>',
renderer: (name, value, type) => { return (<span>SampleRenderer</span>); }
},
{
name: 'Prop3',
displayName: 'String',
type: KanbanTaskMamagedPropertyType.string,
value: 'Hallo World'
}
]
},
{ taskId: 2, title: 'test2', bucket: 'Not Started' },
{ taskId: 3, title: 'test3', bucket: 'Not Started' },
{ taskId: '4', title: 'test 4', bucket: 'Test4' },
{ taskId: '5', title: 'test 5', bucket: 'Test3' },
] ]
}; };
@ -45,7 +71,8 @@ export class MockKanban extends React.Component<IMockKanbanProps, IMockKanbanSta
tasksettings={{ tasksettings={{
showLabels: true, showLabels: true,
showPriority: true, showPriority: true,
showAssignedTo: true showAssignedTo: true,
showTaskDetailsButton: true
} }
} }
taskactions={{ taskactions={{
@ -62,10 +89,10 @@ export class MockKanban extends React.Component<IMockKanbanProps, IMockKanbanSta
private _toggleCompleted(taskId: number | string): void { private _toggleCompleted(taskId: number | string): void {
//TODO //TODO
} }
private _allowMove(taskId: number | string, prevBucket: IKanbanBucket, targetBucket: IKanbanBucket): boolean { private _allowMove(taskId: number | string, prevBucket: IKanbanBucket, targetBucket: IKanbanBucket): boolean {
if(prevBucket.bucket ==='Test2' && targetBucket.bucket ==='Test3') { if (prevBucket.bucket === 'Test2' && targetBucket.bucket === 'Test3') {
return false; return false;
} }
return true; return true;
@ -73,10 +100,10 @@ export class MockKanban extends React.Component<IMockKanbanProps, IMockKanbanSta
private _moved(taskId: number | string, targetBucket: IKanbanBucket): void { private _moved(taskId: number | string, targetBucket: IKanbanBucket): void {
debugger; debugger;
const elementsIndex = findIndex( this.state.tasks ,element => element.taskId == taskId ); const elementsIndex = findIndex(this.state.tasks, element => element.taskId == taskId);
let newArray = [...this.state.tasks]; let newArray = [...this.state.tasks];
newArray[elementsIndex].bucket = targetBucket.bucket; newArray[elementsIndex].bucket = targetBucket.bucket;
this.setState({tasks:newArray}); this.setState({ tasks: newArray });
} }
} }

View File

@ -1,8 +1,9 @@
define([], function() { define([], function() {
return { return {
"CompleteButton": "complete", "CompleteButton": "Complete",
"IsCompleted": "IsCompleted", "IsCompleted": "Is Completed",
"IsNotCompleted": "IsNotCompleted", "IsNotCompleted": "Is Not Completed",
"AddTask": "AddTask" "AddTask": "Add Task",
"OpenDetails":"Open Details"
} }
}); });

View File

@ -3,6 +3,7 @@ declare interface IKanbanBoardStrings {
IsCompleted: string; IsCompleted: string;
IsNotCompleted: string; IsNotCompleted: string;
AddTask: string; AddTask: string;
OpenDetails: string;
} }
declare module 'KanbanBoardStrings' { declare module 'KanbanBoardStrings' {