Usage
This section describes an example implementation of United Code Gantt Pro in an example Oracle APEX application.
The usage guide shows the basic implementation of the plug-in.
Example for basic implementation.
The initial state of page 2 in page designer is presented below:
Implementation Steps
Add new region of type »
Static Content
«Add two date picker items (for example:
P2_START_DATE, P2_END_DATE
). The gantt chart plug-in requires two date picker items in order to work. We also recommend specifying a default value for these two items. In this case even on the first load of the page the gantt chart will be nicely displayed.Add a new Region of type:
United Codes Gantt [Plug-In]
The following region properties are mandatory in order the plug-in to work:
Source (in below example we use SQL query)
Page Items to Submit:
P2_START_DATE
,P2_END_DATE
Under Attributes the following must be set:
- Task ID
- Task name
- Task start date
- Task end date
- Viewpoint start date (P2_START_DATE)
- Viewpoint end date (P2_END_DATE)
6. Below is an example of the page after successfully setting up the plugin:
Advanced Settings (Attributes)
Display events / tasks on timeline
Event/Task Progress
SQL QUERY
select T.ID,
P.ID PROJECT_ID,
P.PROJECT,
T.TASK_NAME ||
case when T.STATUS is not null
then '['||T.STATUS||']'
else null
end TASK_NAME,
T.START_DATE,
T.END_DATE,
T.STATUS,
T.ASSIGNED_TO,
T.COST,
T.BUDGET,
-- Range to have correct calculations is from 0 - 1. (0.50 is 50%)
round(DBMS_RANDOM.VALUE(0,1),2) PROGRESS,
case T.STATUS
when 'On-Hold'
then '#ed6647'
when 'Closed'
then '#68c182'
else null
end COLOR
from EBA_DEMO_CHART_TASKS T
inner join EBA_DEMO_CHART_PROJECTS P
on ( P.ID = T.PROJECT )ATTRIBUTES
Show difference of planning / actual time value (Baseline)
SQL Query
select ID,
PROJECT,
PARENT_TASK,
TASK_NAME,
ROW_VERSION_NUMBER,
START_DATE,
END_DATE,
STATUS,
-- Start date of a planning the task
START_DATE - ROWNUM/ID BASE_START,
-- End date of a planning the task
END_DATE + ROWNUM/ID BASE_END,
ASSIGNED_TO,
COST,
BUDGET,
CREATED,
CREATED_BY,
UPDATED,
UPDATED_BY
from EBA_DEMO_CHART_TASKSAttributes
Drag and drop event/task option & Resize event/task option
Dependency SQL specification
select ID,
PREDECESSOR,
SUCCESSOR,
RELATION,
STATUS,
SHORTDESC
from EBA_DEMO_CHART_TASKS_DEPEND_V;
JSON options
{
"id": "5",
"predecessor": "3",
"successor": "2",
"relation": "finishStart", // "finishStart" | "finishFinish" | "startStart" | "startFinish"
"status": "critical", //used for customization
"shortDesc": "My Description finishStart|critical"
}
Additional event/task information (tooltip)
Hierarchical event/tasks view
SQL Query
select T.ID,
T.PARENT_TASK PARENT_TASK_ID,
P.ID PROJECT_ID,
P.PROJECT,
T.TASK_NAME,
T.START_DATE,
T.END_DATE,
T.STATUS,
T.ASSIGNED_TO,
T.COST,
T.BUDGET,
'{"assigned":"'||T.ASSIGNED_TO||'",
"borderRadius":"10px",
"labelPosition":"start",
"type":"'|| case when T.PARENT_TASK is null
then 'summary' else 'normal' end ||'"
}' CUSTOM_SETTINS
from EBA_DEMO_CHART_TASKS T
inner join EBA_DEMO_CHART_PROJECTS P
on P.ID = T.PROJECTAttributes
Automatic Time Zone
Since Oracle APEX automatically parse date and timestamp into JavScript format (example: "2023-10-28T22:06:00Z") browsers will automatically calculate offset and display it for you.
The ISO format support short notations where the string must only include the date and not time, as in the following formats: YYYY, YYYY-MM, YYYY-MM-DD.
The ISO format does not support time zone names. You can use the Z position to specify an offset from UTC time. If you do not include a value in the Z position, UTC time is used. The correct format for UTC should always include character 'Z' if the offset time value is omitted. The date-parsing algorithms are browser-implementation-dependent and, for example, the date string '2013-02-27T17:00:00' will be parsed differently in Chrome vs Firefox vs IE.
More details can be found in the OJ GANTT documentation.
In order to avoid auto calculation you can disable that option in "Javascript Initialization Code".
function (pOptions) {
//Use browser TimeZone true/false
pOptions.automaticTimezone = false;
return pOptions;
}
Themes
By default, this plug-in select "Redwood notag". In order to be more open for developers to select one of the following theme we introduced new parameter "themeCSS".
Options are :
- '/alta/oj-alta-min.css'
- '/alta/oj-alta-notag-min.css'
- '/alta-android/oj-alta-min.css'
- '/alta-ios/oj-alta-min.css'
- '/alta-windows/oj-alta-min.css'
- '/redwood/oj-redwood-min.css'
- '/redwood/oj-redwood-notag-min.css'
- '/stable/oj-stable-min.css'
To change default theme please set themeCSS in "Javascript Initialization Code".
function (pOptions) {
//Change theme
pOptions.themeCSS = '/stable/oj-stable-min.css';
return pOptions;
}
Timeline, Weekends and Time Buckets
By default, timeline and weekends are enabled and displayed out of the box. But sometimes there is a use case to disable coloring or remove timeline. That is possible with custom options via "Javascript Initialization Code".
function (pOptions) {
//disable timeline
pOptions.display.timeLine = false;
//disable weekend coloring
pOptions.display.weekend = false;
return pOptions;
}
To be even more flexible with coloring timeline you can set current time, multiple times, and add a class on it.
function (pOptions) {
//disable timeline
pOptions.display.timeLineData = [{
value: new Date().toISOString(),
shortDesc: "Current Date",
svgClassName:'demo-current-time-indicator'
}];
return pOptions;
}
Sometimes there is a need to ave different Time Buckets, to display phase of project. You can use declarative option "Time Buckets" and put query in there. This can be useful for coloring sprints, adding holidays, or maybe just adding colored lines where needed.
select TYPE,
START_TIME,-- transformed to "start" used only when type = area
END_TIME, -- transformed to "end" used only when type = area
LINE_TIME, -- transformed to "value" used only when type = line
SHORTDESC,
SVGSTYLE
from YOUR_HOLIDAY_DATES_TABLE;
Or if there specific use case you can also go with setting up Time Bucket JSON in "Javascript Initialization Code".
function (pOptions) {
let firstStart = new Date();
let firstEnd = new Date();
let secondStart = new Date();
let secondEnd = new Date();
firstStart.setMonth(firstStart.getMonth() - 1);
firstEnd.setDate(firstEnd.getDate() - 10);
secondStart.setDate(secondStart.getDate() + 10);
secondEnd.setMonth(secondEnd.getMonth() + 1);
pOptions.display.timeBucketsData = [
{
type: "area",
start: firstStart.toISOString(),
end: firstEnd.toISOString(),
svgStyle: { fill: "#32925e", opacity: "0.18" },
shortDesc: "Time Bucket 1",
},
{
type: "area",
start: secondStart.toISOString(),
end: secondEnd.toISOString(),
svgStyle: { fill: "#eb9632", opacity: "0.18" },
shortDesc: "Time Bucket 2",
},
];
return pOptions;
}
Result:
Javascript Initialization Code
Sometimes you can hit charters imit. In that case declare variable in page "Function and Global Variable Declaration" and reference on it within plug-in "Javascript Initialization Code".
function (pOptions) {
pOptions.gantt = {
"gridlines.horizontal":"visible",
"gridlines.vertical":"visible",
"major-axis.scale":"weeks",
"major-axis.zoom-order":'["quarters", "months", "weeks", "days"]',
"major-axis.converter.weeks":"[[dateConverter]]",
"minor-axis.scale":"days",
"minor-axis.zoom-order":'["quarters", "months", "weeks", "days"]',
"minor-axis.converter.days":"[[diffDaysConverter]]"
"selection-mode":"multiple",
"dnd.move.tasks":"enabled",
"task-defaults.resizable":"enabled",
"style":"width:100%;height:70vh",
"translations.component-name":"Component Name !",
"translations.label-invalid-data":"Something wrong with date fields!",
"task-defaults.height":40,
"task-defaults.border-radius":5,
"task-defaults.label-position":"innerStart",
"task-defaults.type":"summary",
"task-defaults.svg-style":{"fill":"#b366ff"},
"task-defaults.overlap.behavior":"stack"
};
//Use browser TimeZone true/false
pOptions.automaticTimezone = false;
//load additional modules
pOptions.loadModules = ["ojs/ojmenu", "ojs/ojdvttimecomponentscale", "ojs/ojavatar"];
pOptions.methods = {
//Invert resource sort
sortData :
function (data) {
return data.sort(function (a, b) {
return a.resource < b.resource ? 1 : -1;
})
},
// Custom validation, for more details check "Custom validations section"
validateClick : function(task) {
console.log("Validate CLICK on", task);
return false;
},
validateMove : function(task, moveData) {
console.log("Validate MOVE on", task, moveData);
return true;
},
validateResize : function(task, resizeData) {
console.log("Validate RESIZE on", task, resizeData);
return true;
}
/*
sortData : function (data) {
//No sort
return data;
},*/
};
//add elements in region before oj-gantt element
pOptions.elements = [
`<svg height="0" width="0"><defs><marker
id="demoCircleMarker"
viewBox="0 0 12 12" refX="6" refY="6" markerWidth="12" markerHeight="12"
orient="auto"
markerUnits="userSpaceOnUse">
<circle class="demo-circle-marker" cx="6" cy="6" r="5" />
</marker>
<marker
id="demoArrowMarker"
viewBox="0 0 10 10" refX="10" refY="5" markerWidth="12" markerHeight="12"
orient="auto-start-reverse"
markerUnits="userSpaceOnUse">
<path class="demo-arrow-marker" d="M 0 0 L 10 5 L 0 10 z" />
</marker>
<marker
id="demoArrowMarkerCritical"
viewBox="0 0 10 10" refX="10" refY="5" markerWidth="12" markerHeight="12"
orient="auto-start-reverse"
markerUnits="userSpaceOnUse">
<path class="demo-arrow-critical-marker" d="M 0 0 L 10 5 L 0 10 z" />
</marker>
</defs></svg>`
];
//add templates and elements inside oj-gantt element
pOptions.templates = [
`<template slot="rowAxisLabelTemplate" data-oj-as="rowAxisLabel">
<svg class="demo-gantt-row-label">
<g>
<foreignobject :x="0" y="0" width="32" height="32">
<oj-avatar
src="[[rowAxisLabel.data.resourceData.profile]]"
size="xxs"></oj-avatar>
</foreignobject>
<text :x="25" y="19">
<oj-bind-text value="[[rowAxisLabel.data.resource]]"></oj-bind-text>
</text>
</g>
</svg>
</template>`,
`<oj-menu
id="ctxMenu"
slot="contextMenu"
aria-label="Match Edit"
on-oj-menu-action="[[menuItemAction]]"
on-oj-before-open="[[beforeOpenFunction]]">
<oj-option value="Action 1">Action 1</oj-option>
<oj-option value="Action 2">Action 2</oj-option>
<oj-option value="Action 3">Action 3</oj-option>
</oj-menu>
</oj-gantt>`
];
pOptions.callback = function(ViewModel, rowData, viewPoint, ko, ojconverter, TimeUtils) {
// add additional logic and handlers after data is assigned to te ViewModel
ViewModel.dateConverter = ko.observable(new ojconverter.IntlDateTimeConverter({
formatType: "date",
dateFormat: "long",
}));
ViewModel.diffDaysConverter = {
format: (dateString) => {
const day = 24 * 60 * 60 * 1000;
const startDate = new Date(viewPoint.start);
const startTime = startDate.getTime();
const date = new Date(dateString);
return Math.round(Math.abs(date.getTime() - startTime) / day + 1);
}
};
};
return pOptions;
}