Hi All,
I want to add Actual Start, Hours, Progress, Message fields into editor, remove Calendar/AllDay/Repeat/Delete info.
How should I do in the following config. In addition, if I want to save these values of new fields, where should I add saveHandler function?
CalendarConfig.ts
//import { Calendar } from '@bryntum/calendar';
import { type BryntumCalendarProps } from '@bryntum/calendar-react';
import AppEventModel from './AppEventModel';
//import './CalendarToolbar';
export function formatDateToDDMMYYYY(date: string | Date): string | undefined {
if (!date) return undefined;
const dateObj = new Date(date);
const day = String(dateObj.getDate()).padStart(2, '0');
const month = String(dateObj.getMonth() + 1).padStart(2, '0'); // month start from 0
const year = dateObj.getFullYear();
//const hour = String(dateObj.getHours()).padStart(2, '0');
//const minute = String(dateObj.getMinutes()).padStart(2, '0');
//const second = String(dateObj.getSeconds()).padStart(2, '0');
//return `${year}-${month}-${day}T${hour}:${minute}:${second}`;
return `${year}-${month}-${day}`;
}
const calendarProps: BryntumCalendarProps = {
// Features named by the properties are included.
// An object is used to configure the feature.
timeRangesFeature: {
// configure timeRanges feature...
headerWidth: 42
},
// An object is used to configure the feature.
eventTooltipFeature: {
align: 'l-r'
},
date: new Date(),//new Date(2020, 9, 11),
hideNonWorkingDays: false,
// // Modes are the views available in the Calendar.
// // An object is used to configure the view.
// modes : {
// year : false
// }
// Show the event list
modes: {
year: false,
list: {
title: 'Task List', // The title of the list button
weight: 100,
// If we use field names which the EventList creates for itself, our config
// gets merged into the default, so we can affect the EventList's own columns.
columns: [{
type: 'column',
field: 'name',
flex: '0 0 12em',
renderer({ record }) {
const event = record as AppEventModel;
return [{
tag: 'i',
className: 'b-icon b-icon-circle',
style: `color:${event.resource?.eventColor}`
}, {
text: event.name
}];
}
},
{
type: 'column',
field: 'projectId',
text: 'Project Id',
flex: '0 0 8em',
renderer({ record }) {
const event = record as AppEventModel;
return [{
tag: 'i',
//className: 'b-icon b-icon-circle',
style: `color:${event.resource?.eventColor}`
}, {
text: (event as any).projectId
}];
}
},
{
type: 'column',
field: 'projectName',
text: 'Project Name',
flex: '0 0 12em',
renderer({ record }) {
const event = record as AppEventModel;
return [{
tag: 'i',
//className: 'b-icon b-icon-circle',
style: `color:${event.resource?.eventColor}`
}, {
text: (event as any).projectName
}];
}
},
{
type: 'column',
field: 'startDate',
text: 'Plan Start',
flex: '0 0 8em',
renderer({ record }) {
const event = record as AppEventModel;
return [{
tag: 'i',
//className: 'b-icon b-icon-circle',
style: `color:${event.resource?.eventColor}`
}, {
text: formatDateToDDMMYYYY(event.startDate)
}];
}
},
{
type: 'column',
field: 'endDate',
text: 'Plan End',
flex: '0 0 8em',
renderer({ record }) {
const event = record as AppEventModel;
return [{
tag: 'i',
//className: 'b-icon b-icon-circle',
style: `color:${event.resource?.eventColor}`
}, {
text: formatDateToDDMMYYYY(event.endDate)
}];
}
},
{
type: 'column',
field: 'actualStart',
text: 'Actual Start',
flex: '0 0 8em',
renderer({ record }) {
const event = record as AppEventModel;
return [{
tag: 'i',
//className: 'b-icon b-icon-circle',
style: `color:${event.resource?.eventColor}`
}, {
text: (event as any).actualStart
}];
}
},
{
type: 'column',
field: 'hour',
text: 'Hours',
flex: '0 0 12em',
renderer({ record }) {
const event = record as AppEventModel;
return [{
tag: 'i',
//className: 'b-icon b-icon-circle',
style: `color:${event.resource?.eventColor}`
}, {
text: (event as any).hour
}];
}
},
{
type: 'column',
field: 'progress',
text: 'Progress',
flex: '0 0 12em',
renderer({ record }) {
const event = record as AppEventModel;
return [{
tag: 'i',
//className: 'b-icon b-icon-circle',
style: `color:${event.resource?.eventColor}`
}, {
text: (event as any).progress
}];
}
},
{
type: 'column',
field: 'message',
text: 'message',
flex: '0 0 12em',
},
{
type: 'column',
field: 'resources',
text: 'Assignee',
flex: '0 0 12em',
renderer({ record }) {
const event = record as AppEventModel;
return [{
tag: 'i',
//className: 'b-icon b-icon-circle',
style: `color:${event.resource?.eventColor}`
}, {
text: event.resource !== undefined && event.resource !== null ? event.resource.name : '' //event.resourceId
}];
}
},
],
features: {
headerMenu: {
// We can't group by other fields, so disable all grouping menu options
items: {
groupAsc: false,
groupDesc: false,
groupRemove: false
}
},
}
},
day: {
weight: 200,
dayStartTime: 8,
dayEndTime: 22,
hourHeight: 70
},
week: {
weight: 201,
dayStartTime: 8,
dayEndTime: 22,
hourHeight: 70
},
month:{
weight: 202,
},
agenda: {
weight: 203,
},
weekResources: {
weight: 204,
// Type has the final say over which view type is created
type: 'resource',
title: 'By Resources',
}
},
mode: 'list',// list, week // Default display
cls: 'custom-styles',
tbar: {
items: {
addButton: {
type: 'button',
text: 'Non-Working Time',
//weight : 100, // Add before the Today button
//weight: 600, // Add before the Day view
weight: 101, // Add before the Day view
onClick({ source }) {
calendar.project.timeRangeStore.add({
name: 'no-working',
startDate: new Date(),
endDate: new Date(Date.now() + 3600 * 1000 * 2),
color: 'red',
cls: 'hatch-small b-cal-timerange-stretch',
recurrenceRule: 'FREQ=DAILY;INTERVAL=1;COUNT=10'
});
}
}
}
},
};
export { calendarProps };
CustomCalendar.tsx
import { createElement, Fragment, ReactElement, useRef, useState, useMemo } from 'react';
import {
BryntumCalendarProjectModel, BryntumCalendar,
} from '@bryntum/calendar-react';
import {
calendarProps,
} from './components/CalendarConfig';
import { CustomCalendarContainerProps } from "../typings/CustomCalendarProps";
import { projectData } from './components/AppData';
import { ObjectItem, ValueStatus } from "mendix";
import './ui/CustomCalendar.scss';
import {
ResourceModel,
TimeRangeModelConfig
} from '@bryntum/calendar';
const mapMxID = (mxID: string | Big | undefined): string | undefined =>
mxID ? (typeof mxID === "string" ? mxID : mxID.toString()) : undefined;
export interface ITask {
obj: ObjectItem;
id: string;
projectId: string;
projectName: string;
progress: number | undefined,
hour: number | undefined,
message: string,
actualStart: Date | undefined
name: string | undefined;
startDate: Date | undefined;
endDate: Date | undefined;
resourceId: string,
eventColor: string
}
export interface IResource {
obj: ObjectItem;
id: string;
resourceName: string | undefined;
resourceRole: string | undefined;
}
function formatDateToDDMMYYYY(date: string | Date | undefined): string | undefined {
if (!date || date === undefined) return undefined;
const dateObj = new Date(date);
const day = String(dateObj.getDate()).padStart(2, '0');
const month = String(dateObj.getMonth() + 1).padStart(2, '0'); // 月份从0开始
const year = dateObj.getFullYear();
return `${year}-${month}-${day}`;
}
export function convertTask2Event(oldTasks: ITask[]): any[] {
let eventArray: any[] = [];
for (const oldTask of oldTasks) {
console.info("convertTask2Event-", formatDateToDDMMYYYY(oldTask.startDate ?? new Date()));
const eventItem = {
id: oldTask.id,
name: oldTask.name,
projectId: oldTask.projectId,
projectName: oldTask.projectName,
progress: oldTask.progress,
hour: oldTask.hour,
message: oldTask.message,
actualStart: oldTask.id === undefined ? undefined : formatDateToDDMMYYYY(oldTask.actualStart ?? undefined),
startDate: oldTask.id === undefined ? undefined : formatDateToDDMMYYYY(oldTask.startDate ?? ''),
endDate: oldTask.id === undefined ? undefined : formatDateToDDMMYYYY(oldTask.endDate ?? ''),
resourceId: oldTask.resourceId,
eventColor: oldTask.eventColor
};
eventArray.push(eventItem);
}
return eventArray;
}
export function convertResourceList(resources: IResource[]) {
let newResources: ResourceModel[] = [];
for (const resource of resources) {
if (resource.resourceName !== undefined && resource.resourceRole !== undefined) {
const newResource = new ResourceModel({
id: resource.id,
name: resource.resourceName,
//taskRole: resource.resourceRole,
//events: [],
//allowOverlap: false,
//barMargin: 0
});
newResources.push(newResource);
}
//console.info("convertResourceList", newResources);
}
return newResources;
}
export function CustomCalendar(props: CustomCalendarContainerProps): ReactElement {
const calendarRef = useRef<BryntumCalendar>(null);
const project = useRef<BryntumCalendarProjectModel>(null);
const [events] = useState(projectData.events);
//const [resources] = useState(projectData.resources);
const [timeRanges] = useState<TimeRangeModelConfig>(projectData.timeRanges.rows as unknown as TimeRangeModelConfig);
//const [teamUserSet, setTeamUserSet] = useState<IResource[]>([]);
console.info(props.sampleText);
const resourceList = useMemo(() => {
if (props.resourceList.status === ValueStatus.Available && props.resourceList.items) {
const iResourceList: IResource[] = [];
for (const objItem of props.resourceList.items) {
const newId = mapMxID(props.resourceId.get(objItem).value);
if (newId) {
const newResource: IResource = {
obj: objItem,
//id: newId,
//id: props.resourceName.get(objItem).value || '1',
id: props.resourceId.get(objItem).value?.toString() ?? '1',
resourceName: props.resourceName.get(objItem).value,
resourceRole: props.resourceRole.get(objItem).value
};
iResourceList.push(newResource);
}
}
//setTeamUserSet(iResourceList);
return convertResourceList(iResourceList);
}
}, [props.resourceList,
props.resourceName,
props.resourceRole,
props.resourceId
]);
const events2 = useMemo(() => {
if (props.objectList.status === ValueStatus.Available && props.objectList.items) {
const flatTaskList: ITask[] = [];
// Iterator through the Mendix data and make a flat array of tasks (all children attributes are empty)
for (const objItem of props.objectList.items) {
const newId = mapMxID(props.id.get(objItem).value); // Example of how to parse the data from Mendix
// only create a task if it's ID is valid
if (newId) {
const newTask: ITask = {
obj: objItem,
id: newId,
projectId: props.projectId.get(objItem).value ?? 'NULL',
projectName: props.projectName.get(objItem).value ?? 'NULL',
progress: props.progress.get(objItem).value?.toNumber() ?? undefined,
hour: props.hour.get(objItem).value?.toNumber() ?? undefined,
message: props.message.get(objItem).value ?? '',
actualStart: props.actualStart.get(objItem).value,
startDate: props.planStartDate.get(objItem).value,
endDate: props.planEndDate.get(objItem).value,
name: props.taskName.get(objItem).value ?? 'NULL',
//resourceId: props.taskOwner.get(objItem).value || '1',//'bryntum',
resourceId: ( props.projectId.get(objItem).value ?? 'NULL') + '-' + props.taskOwner.get(objItem).value || '1',//'bryntum',
eventColor: 'green'
};
flatTaskList.push(newTask);
}
}
console.info(`useMemo processed flatTaskList count=${flatTaskList.length} `, flatTaskList);
if (flatTaskList) {
return convertTask2Event(flatTaskList);
}
else {
console.info("useMemo no task found with parentId undefined");
return undefined;
}
}
}, [props.objectList,
props.id,
props.taskName,
props.planStartDate,
props.planEndDate]);
let bryntumEvents = events2 != undefined ? events2 : [];
console.info("bryntumEvents", bryntumEvents);
console.info("events", events);
let resourcesCollection = resourceList != undefined ? resourceList : [];
return (
<div id="container">
<Fragment>
<BryntumCalendarProjectModel
ref={project}
//events={events}
events={bryntumEvents}
//resources={resources}
resources={resourcesCollection}
timeRanges={Array.isArray(timeRanges) ? timeRanges : undefined}
/>
<BryntumCalendar
ref={calendarRef}
project={project}
{...calendarProps}
/>
</Fragment>
</div>
);
}