Overview
How to configure the Scheduler component to display task priority.
Groups tasks with the same priority and display them in the specified order in dedicated sub-rows of the assigned resource.
Optionally, the row headers can display the priority number for each sub-row.
The attached project includes a trial version of DayPilot Pro for JavaScript (see License below).
License
Licensed for testing and evaluation purposes. Please see the license agreement included in the sample project. You can use the source code of the tutorial if you are a licensed user of DayPilot Pro for JavaScript. Buy a license.
Displaying Concurrent Tasks with Different Priorities in the JavaScript Scheduler
Let’s start with a list of resources that will be displayed as rows in the JavaScript Scheduler component:
const resources = [
{name: "Resource 1", id: "R1"},
{name: "Resource 2", id: "R2"},
{name: "Resource 3", id: "R3"},
{name: "Resource 4", id: "R4"},
{name: "Resource 5", id: "R5"},
{name: "Resource 6", id: "R6"},
{name: "Resource 7", id: "R7"},
{name: "Resource 8", id: "R8"},
{name: "Resource 9", id: "R9"},
];
If you have multiple overlapping tasks with different priorities defined for each row, they will be arranged in the appropriate row using the default event sorting rule (which is start asc, end desc
).
const events = [
{id: 1, text: "Task 1", start: firstDay.addDays(3), end: firstDay.addDays(9), resource: "R1", priority: 1},
{id: 2, text: "Task 2", start: firstDay.addDays(2), end: firstDay.addDays(8), resource: "R1", priority: 2},
{id: 3, text: "Task 3", start: firstDay.addDays(1), end: firstDay.addDays(7), resource: "R1", priority: 3},
{id: 4, text: "Task 4", start: firstDay.addDays(10), end: firstDay.addDays(13), resource: "R1", priority: 1},
];
To make the priority of each task cleary visible, we will set the background color accordingly and display the priority number in an active area displayed on the right side of the task.
This can be done using the onBeforeEventRender event handler:
onBeforeEventRender: args => {
const event = args.data;
const priority = event.priority;
switch (priority) {
case 1:
args.data.backColor = "#ea735e";
break;
case 2:
args.data.backColor = "#f6b26b";
break;
case 3:
args.data.backColor = "#93c47d";
break;
default:
args.data.backColor = "#555555";
}
args.data.borderColor = "darker";
args.data.fontColor = "#ffffff";
args.data.areas = [
{
right: 5,
top: 5,
width: 25,
height: 25,
text: priority,
style: "text-align: center; line-height: 25px;",
backColor: "#333",
fontColor: "#ffffff",
borderRadius: "50%",
}
]
}
The default configuration of the Scheduler may result in a view where tasks with the same priority are displayed at different positions within a row:
There are a couple of event arrangement strategies available that may help (such as forcing a specific line within a row for each event or using virtual containers), but we will take a look at another option, which is to define a dedicated sub-row for each priority level.
Defining Sub-Rows for Tasks Priorities in the Scheduler UI
We will split each resource into three sub-rows, one for each priority level.
In this example, we use a static array with pre-defined resources. If you load your resources from the server side, you can pre-process the array before loading it into the Scheduler component, just as we do it below.
const resources = [
{name: "Resource 1", id: "R1"},
{name: "Resource 2", id: "R2"},
{name: "Resource 3", id: "R3"},
{name: "Resource 4", id: "R4"},
{name: "Resource 5", id: "R5"},
{name: "Resource 6", id: "R6"},
{name: "Resource 7", id: "R7"},
{name: "Resource 8", id: "R8"},
{name: "Resource 9", id: "R9"},
];
resources.forEach(r => {
r.split = [
{name: "Priority 1", id: r.id + ".1", priority: 1},
{name: "Priority 2", id: r.id + ".2", priority: 2},
{name: "Priority 3", id: r.id + ".3", priority: 3}
];
});
Now we will define row header columns where the first column displays the parent resource name and the second column displays the priority.
Adding the split
property to the column definition ensures that this column will display data for the sub-resource.
const scheduler = new DayPilot.Scheduler("scheduler", {
rowHeaderColumns: [
{name: "Resource", display: "name"},
{name: "Priority", display: "name", split: true}
],
// ...
});
We need to modify our task data to use a resource
field that includes the priority specifier. For example, "R1"
resource in a task with priority 1
will become "R1.1"
. This ensures that the task will be rendered in the correct sub-resource.
const events = [
{id: 1, text: "Task 1", start: firstDay.addDays(3), end: firstDay.addDays(9), resource: "R1", priority: 1},
{id: 2, text: "Task 2", start: firstDay.addDays(2), end: firstDay.addDays(8), resource: "R1", priority: 2},
{id: 3, text: "Task 3", start: firstDay.addDays(1), end: firstDay.addDays(7), resource: "R1", priority: 3},
{id: 4, text: "Task 4", start: firstDay.addDays(10), end: firstDay.addDays(13), resource: "R1", priority: 1},
];
events.forEach(e => {
const priority = e.priority;
e.resource += `.${priority}`;
});
This setup ensures that tasks with the same priority will always be displayed at the same position. You can event have multiple overlapping tasks with the same priority, and the Scheduler will sort them in the dedicated sub-row using the default rule:
Hide the Priority from the Row Header
It is also possible to keep just the resources name in the row headers (without having a special header for each priority level). Just remove (or hide) the column from the row header:
rowHeaderColumns: [
{name: "Resource", display: "name"},
// {name: "Priority", display: "name", split: true}
],
This will result in a more compact Scheduler view:
Full Source Code
Here is the full source code of our Scheduler configuration that defines a special sub-resource for each priority level so that tasks with the same priority can be displayed at the same position:
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8"/>
<meta name="viewport" content="width=device-width, initial-scale=1">
<title>JJavaScript Scheduler: Using Split Resources to Manage Task Priorities</title>
<style type="text/css">
p, body, td, input, select, button { font-family: -apple-system,system-ui,BlinkMacSystemFont,'Segoe UI',Roboto,'Helvetica Neue',Arial,sans-serif; font-size: 14px; }
body { padding: 0px; margin: 0px; background-color: #ffffff; }
a { color: #1155a3; }
.space { margin: 10px 0px 10px 0px; }
.header { background: #003267; background: linear-gradient(to right, #011329 0%,#00639e 44%,#011329 100%); padding:20px 10px; color: white; box-shadow: 0px 0px 10px 5px rgba(0,0,0,0.75); }
.header a { color: white; }
.header h1 a { text-decoration: none; }
.header h1 { padding: 0px; margin: 0px; }
.main { padding: 10px; margin-top: 10px; }
.generated { color: #999; }
.generated a { color: #999; }
</style>
<link href="css/toolbar.css" type="text/css" rel="stylesheet" />
<!-- DayPilot library -->
<script src="js/daypilot/daypilot-all.min.js"></script>
</head>
<body>
<div class="header">
<h1><a href='https://code.daypilot.org/46527/javascript-scheduler-managing-priorities-with-split-resources'>JavaScript Scheduler: Using Split Resources to Manage Task Priorities</a></h1>
<div><a href="https://javascript.daypilot.org/">DayPilot for JavaScript</a> - HTML5 Calendar/Scheduling Components for JavaScript/Angular/React/Vue</div>
</div>
<div class="main">
<div class="toolbar">
<label><input type="checkbox" id="show" checked>Show priorities in row headers</label>
</div>
<div id="scheduler"></div>
<div class="generated">Generated using <a href="https://builder.daypilot.org/">DayPilot UI Builder</a>.</div>
</div>
<script>
const scheduler = new DayPilot.Scheduler("scheduler", {
timeHeaders: [{"groupBy":"Month"},{"groupBy":"Day","format":"d"}],
scale: "Day",
days: DayPilot.Date.today().daysInMonth(),
startDate: DayPilot.Date.today().firstDayOfMonth(),
cellWidth: 50,
durationBarVisible: false,
eventBorderRadius: 20,
eventPadding: 5,
rowMarginTop: 2,
rowMarginBottom: 2,
rowHeaderColumns: [
{name: "Resource", display: "name"},
{name: "Priority", display: "name", split: true}
],
timeRangeSelectedHandling: "Enabled",
onTimeRangeSelected: async (args) => {
const scheduler = args.control;
const modal = await DayPilot.Modal.prompt("Create a new event:", "Event 1");
scheduler.clearSelection();
if (modal.canceled) { return; }
scheduler.events.add({
start: args.start,
end: args.end,
id: DayPilot.guid(),
resource: args.resource,
text: modal.result
});
},
onBeforeEventRender: args => {
const event = args.data;
const priority = event.priority;
switch (priority) {
case 1:
args.data.backColor = "#ea735e";
break;
case 2:
args.data.backColor = "#f6b26b";
break;
case 3:
args.data.backColor = "#93c47d";
break;
default:
args.data.backColor = "#555555";
}
args.data.borderColor = "darker";
args.data.fontColor = "#ffffff";
args.data.areas = [
{
right: 5,
top: 5,
width: 25,
height: 25,
text: priority,
style: "text-align: center; line-height: 25px;",
backColor: "#333",
fontColor: "#ffffff",
borderRadius: "50%",
}
]
},
onEventMove: args => {
const event = args.e.data;
const row = scheduler.rows.find(args.newResource);
event.priority = row.data.priority;
}
});
scheduler.init();
const app = {
elements: {
show: document.getElementById("show")
},
addEventHandlers() {
this.elements.show.addEventListener("change", () => {
scheduler.rowHeaderColumns[1].hidden = !this.elements.show.checked;
scheduler.update();
});
},
loadData() {
const resources = [
{name: "Resource 1", id: "R1"},
{name: "Resource 2", id: "R2"},
{name: "Resource 3", id: "R3"},
{name: "Resource 4", id: "R4"},
{name: "Resource 5", id: "R5"},
{name: "Resource 6", id: "R6"},
{name: "Resource 7", id: "R7"},
{name: "Resource 8", id: "R8"},
{name: "Resource 9", id: "R9"},
];
resources.forEach(r => {
r.split = [
{name: "Priority 1", id: r.id + ".1", priority: 1},
{name: "Priority 2", id: r.id + ".2", priority: 2},
{name: "Priority 3", id: r.id + ".3", priority: 3}
];
});
const firstDay = DayPilot.Date.today().firstDayOfMonth();
const events = [
{id: 1, text: "Task 1", start: firstDay.addDays(3), end: firstDay.addDays(9), resource: "R1", priority: 1},
{id: 2, text: "Task 2", start: firstDay.addDays(2), end: firstDay.addDays(8), resource: "R1", priority: 2},
{id: 3, text: "Task 3", start: firstDay.addDays(1), end: firstDay.addDays(7), resource: "R1", priority: 3},
{id: 4, text: "Task 4", start: firstDay.addDays(10), end: firstDay.addDays(13), resource: "R1", priority: 1},
];
events.forEach(e => {
const priority = e.priority;
e.resource += `.${priority}`;
});
scheduler.update({resources, events});
},
init() {
this.loadData();
this.addEventHandlers();
}
};
app.init();
</script>
</body>
</html>
You can download the HTML/JavaScript project using the “Download” button at the top of this tutorial.