The Timeline View PCF control requires specific App Settings to be configured for proper functionality. These settings are divided into required and optional configurations.
Required Settings
The following settings are mandatory and must be configured for the control to function:
Roster/TimelinePCF/Config- Contains the main configuration parameters for the timeline controlRoster/TimelinePCF/Datasources- Defines the data sources that the timeline will display
Optional Settings
The following settings provide additional customisation and functionality:
Roster/TimelinePCF/FetchXml- Custom FetchXML queries for data retrievalRoster/TimelinePCF/Options- Additional display and behavior optionsRoster/TimelinePCF/TransformFunction- Custom data transformation logic
Configuration Details
In the following sections, we will explain how to configure each of these settings in detail.
Roster/TimelinePCF/Config
This setting contains the main configuration parameters for the timeline control. The configuration must be a JSON object.
Required Properties
render- A function that generates the display content for timeline itemsTakes item data as input
Returns an object with:
content- String or HTML element to displayclassName- Optional CSS class namestyle- Optional inline stylesentity- Optional entity information (name, id, formId)
Optional Properties
groups- Array of group functions to organize timeline itemsEach function takes item data and returns group information with:
id- Unique group identifiercontent- Group display nameentity- Optional entity information
groupOrder- Array of functions to define group orderingEach function compares two groups and returns a number for sorting
order- Function to determine item ordering within groupsTakes two items and returns a number for sorting
name- Optional name for the configurationcollapsed- Boolean to set default collapsed statecss- Optional CSS string for custom stylingThese classes can be used in the render function for custom item styles
Default Value Configuration
The following is the default configuration used by the Timeline View control when no custom configuration is provided:
{
collapsed: false,
render: (segment) => ({
entity: {
name: "vel_rosterpositionallocationsegment",
id: segment.entityid,
},
content:
"<div><div>" +
(segment[
"_vel_resourceid_value@OData.Community.Display.V1.FormattedValue"
] ?? "Unallocated") +
"</div><div>" +
segment["startdate@OData.Community.Display.V1.FormattedValue"] +
(segment.maxDate
? " - " + new Date(segment.enddate).toLocaleDateString("en-AU")
: "") +
"</div></div>",
}),
name: "Segments",
groups: [
(segment) => {
if (!segment["_vel_resourceid_value"]) {
return {
id: "Unallocated",
content: "Unallocated",
};
}
return {
id: segment["_vel_rosterpositionid_value"],
content:
segment[
"_vel_rosterpositionid_value@OData.Community.Display.V1.FormattedValue"
],
};
},
(segment) => ({
id:
(segment["_vel_resourceid_value"] ?? "") +
segment["_vel_rosterpositionallocationid_value"],
content:
segment[
"_vel_rosterpositionallocationid_value@OData.Community.Display.V1.FormattedValue"
],
}),
],
groupOrder: [
(a, b) =>
a.content === "Unallocated"
? 1
: b.content === "Unallocated"
? -1
: a.content.localeCompare(b.content),
],
css: `
/* Custom styles for vis.js Timeline by Velrada
https://visjs.github.io/vis-timeline/docs/timeline/#Styles
With simulated Fluent UI styling:
https://developer.microsoft.com/en-us/fluentui#/
*/
... more css
`
}
Default Configuration Breakdown
Render Function - Displays roster positional allocation segments as timeline items - Shows the assigned resource name (or "Unallocated" if no resource is assigned) - Displays the start date and end date of the allocation - Links each item to the vel_rosterpositionallocationsegment entity for navigation
Group Structure The configuration creates a two-level hierarchy:
First Level Groups (Roster Positions): Groups segments by roster position
Unallocated segments are grouped under "Unallocated"
Allocated segments are grouped by their roster position
Second Level Groups (Position Allocations): Sub-groups segments by specific position allocation
Creates unique groups for each resource-position allocation combination
Uses the position allocation's display name as the group label
Group Ordering - Places "Unallocated" groups at the top of the list - Orders all other groups alphabetically by their display name - Ensures consistent and predictable group arrangement
Visual Styling - Includes custom CSS that applies Fluent UI design patterns - Integrates with vis.js Timeline component styling - Provides a consistent look and feel with Power Platform applications
This default configuration is specifically designed for roster management scenarios where you need to visualize resource allocations across different positions and time periods.
Roster/TimelinePCF/Datasources
This setting defines the data sources that the timeline will display. The configuration must be a JSON array containing data source objects that follow the DataSource schema.
Data Source Properties
id(required) - Unique identifier for the data sourcefetchXml(optional) - FetchXML query string or App Setting reference to retrieve dataparams(optional) - Key-value pairs for parameterizing FetchXML queriesSpecial parameters:
startDate,endDate,entityIdParameter references: Use
$dataSourceId.propertyNameto reference data from other sources
entities(optional) - Cached entity data retrieved from the queryerror(optional) - Error message if data loading failshidden(optional) - Boolean to hide data source from timeline displaypreload(optional) - Boolean to load this data source before others (for dependencies)searchKeys(optional) - Array of field names to use for search functionality
Parameter Substitution
FetchXML queries support dynamic parameter substitution:
{{entityId}}- Current entity record ID{{startDate}}- Timeline start date{{endDate}}- Timeline end dateCustom parameters - Any key from the
paramsobject
Data Source References
You can reference data from one source in another using the $ syntax: - $sourceId.propertyName - Gets property values from the referenced data source - Values are formatted as <value>property</value> for FetchXML conditions
Example Configuration
[
{
"id": "positions",
"fetchXml": "Roster/TimelinePCF/PositionsFetch",
"preload": true,
"searchKeys": ["vel_name", "vel_code"]
},
{
"id": "allocations",
"fetchXml": "<fetch><entity name='vel_allocation'><filter><condition attribute='vel_positionid' operator='in'>{{positionIds}}</condition></filter></entity></fetch>",
"params": {
"positionIds": "$positions.vel_positionid"
},
"searchKeys": ["vel_resourcename"]
},
{
"id": "hidden-reference",
"fetchXml": "Roster/TimelinePCF/ReferenceFetch",
"hidden": true,
"preload": true
}
]
This configuration creates three data sources: one for positions (preloaded), one for allocations that depends on position data, and one hidden reference source.
Default Value Configuration
[
{
id: 'segments',
fetchXml: 'Roster/TimelinePCF/FetchXml',
searchKeys: ['PositionName', 'ResourceName']
},
]
Default Roster/TimelinePCF/FetchXml
The default FetchXML query retrieves roster positional allocation segments with related data for timeline display:
<fetch distinct='true'>
<entity name='vel_rosterpositionallocationsegment'>
<attribute name='statecode'/>
<attribute name='vel_name'/>
<attribute name='vel_resourceid'/>
<attribute name='vel_rosterpositionallocationid'/>
<attribute name='vel_processingstatus'/>
<attribute name='vel_patternbehaviour'/>
<attribute name='vel_startdate' alias='startdate'/>
<attribute name='vel_customday01'/>
<attribute name='vel_rosterpositionid'/>
<attribute name='vel_rosterpatternid'/>
<attribute name='vel_rosterid'/>
<attribute name='statuscode'/>
<attribute name='vel_overridemanualassigns'/>
<attribute name='vel_rosterpositionallocationsegmentid' alias='entityid'/>
<order attribute='vel_startdate' />
<filter>
<condition attribute="statecode" operator="eq" value="0" />
<condition attribute='vel_rosterid' operator='eq' value='{{entityId}}'/>
</filter>
<link-entity name='vel_rosterpositionallocation' from='vel_rosterpositionallocationid' to='vel_rosterpositionallocationid' link-type='inner' alias='allocation'>
<attribute name='vel_allocationstartdate'/>
<attribute name='vel_allocationenddate'/>
</link-entity>
<link-entity name="vel_rosterposition" from="vel_rosterpositionid" to="vel_rosterpositionid" alias="Position">
<attribute name="vel_personaid"/>
<attribute name="vel_name" alias="PositionName" />
</link-entity>
<link-entity name="bookableresource" from="bookableresourceid" to="vel_resourceid" alias="Resource">
<attribute name="name" alias="ResourceName" />
</link-entity>
</entity>
</fetch>
FetchXML Breakdown
Main Entity: vel_rosterpositionallocationsegment - Retrieves the core timeline segments that represent allocation periods - Uses distinct='true' to eliminate duplicate records from joins - Ordered by vel_startdate to ensure chronological display
Key Attributes Retrieved: - vel_startdate (aliased as 'startdate') - Timeline item start time - vel_rosterpositionallocationsegmentid (aliased as 'entityid') - Unique identifier for navigation - vel_resourceid - Links to assigned resource - vel_rosterpositionallocationid - Links to position allocation - vel_rosterpositionid - Links to roster position - Status and processing fields for business logic
Filter Conditions: - statecode = 0 - Only active records - vel_rosterid = {{entityId}} - Segments for the current roster (parameter substitution)
Linked Entities:
vel_rosterpositionallocation (alias: 'allocation')
Provides allocation start/end dates
Inner join ensures only segments with valid allocations
vel_rosterposition (alias: 'Position')
Provides position details and person assignment
vel_namealiased as 'PositionName' for displayUsed in grouping logic
bookableresource (alias: 'Resource')
Provides resource information for assigned personnel
namealiased as 'ResourceName' for displayUsed in timeline item rendering and search
Data Flow: 1. Query starts with roster segments for the specified roster 2. Joins allocation data to get timing information 3. Joins position data for grouping and display 4. Joins resource data for personnel assignment display 5. Results ordered chronologically for timeline rendering
This query structure supports the default configuration's grouping (by position and allocation) and rendering (showing resource names and dates) requirements.
Roster/TimelinePCF/Options
This optional setting allows you to customize the behavior and appearance of the vis.js Timeline component used by the Timeline View control. The configuration must be a JSON object containing vis.js Timeline options.
Common Timeline Options
Display Options: - orientation - Controls timeline orientation (e.g., "top", "bottom", "both") - showCurrentTime - Boolean to display current time indicator - showTooltips - Boolean to enable/disable item tooltips - stack - Boolean to enable item stacking within groups - zoomable - Boolean to enable zoom functionality - moveable - Boolean to enable pan/move functionality
Time Range Options: - start - Timeline start date/time - end - Timeline end date/time - min - Minimum zoom level (earliest date) - max - Maximum zoom level (latest date)
Grouping Options: - groupOrder - Function or string to determine group ordering - groupTemplate - Function to customize group label rendering
Interaction Options: - selectable - Boolean to enable item selection - multiselect - Boolean to enable multiple item selection - editable - Object with editable properties (add, updateTime, updateGroup, remove)
Visual Options: - height - Timeline height (e.g., "400px", "100%") - width - Timeline width - margin - Margin configuration object - template - Function to customize item rendering
Example Configuration
{
"orientation": "top",
"showCurrentTime": true,
"zoomable": true,
"moveable": true,
"stack": true,
"height": "500px",
"start": "2024-01-01",
"end": "2024-12-31",
"editable": {
"add": false,
"updateTime": true,
"updateGroup": false,
"remove": false
},
"selectable": true,
"multiselect": false,
"showTooltips": true,
"margin": {
"item": {
"horizontal": 10,
"vertical": 5
}
}
}
Default Options
If no options are specified, the Timeline View uses vis.js default settings with minimal customization for Power Platform integration.
For a complete list of available options, refer to the vis.js Timeline documentation.
Roster/TimelinePCF/TransformFunction
This optional setting allows you to define a custom JavaScript function that transforms the raw data from all data sources before it's displayed on the timeline. This is useful for complex data manipulation, filtering, or combining data from multiple sources.
Function Signature
The transform function must follow this signature:
(dataSources: DataSource[]) => any[]
Input: Array of
DataSourceobjects containing the loaded entitiesOutput: Array of transformed objects that will be used as timeline items
Function Requirements
Must be a valid JavaScript function string
Should return an array of objects suitable for timeline display
Each returned object should contain properties needed by the render function
Function is validated against the
TransformFunctionSchemabefore execution
Data Source Structure
Each data source in the input array contains: - id - Data source identifier - entities - Array of loaded entity records - hidden - Whether the data source is hidden from display - Other data source properties
Use Cases
Data Filtering and Sorting:
(dataSources) => {
const segments = dataSources.find(ds => ds.id === 'segments')?.entities || [];
return segments
.filter(segment => segment.statuscode === 1)
.sort((a, b) => new Date(a.startdate) - new Date(b.startdate));
}
Combining Multiple Data Sources:
(dataSources) => {
const segments = dataSources.find(ds => ds.id === 'segments')?.entities || [];
const allocations = dataSources.find(ds => ds.id === 'allocations')?.entities || [];
return segments.map(segment => ({
...segment,
allocationInfo: allocations.find(alloc =>
alloc.vel_rosterpositionallocationid === segment._vel_rosterpositionallocationid_value
)
}));
}
Data Aggregation:
(dataSources) => {
const segments = dataSources.find(ds => ds.id === 'segments')?.entities || [];
// Group segments by position and merge overlapping periods
const grouped = segments.reduce((acc, segment) => {
const positionId = segment._vel_rosterpositionid_value;
if (!acc[positionId]) acc[positionId] = [];
acc[positionId].push(segment);
return acc;
}, {});
return Object.values(grouped).flat();
}
Adding Calculated Fields:
(dataSources) => {
const segments = dataSources.find(ds => ds.id === 'segments')?.entities || [];
return segments.map(segment => ({
...segment,
duration: segment.enddate ?
(new Date(segment.enddate) - new Date(segment.startdate)) / (1000 * 60 * 60) : 0,
isOvertime: segment.vel_customday01 > 8
}));
}
Default Behavior
When no transform function is provided, the Timeline View: 1. Combines all non-hidden data sources 2. Merges all entity arrays into a single array 3. Passes the combined data directly to the render function
Error Handling
Invalid functions will be ignored and logged to the console
Runtime errors in the transform function will fall back to default behavior
Function validation occurs during control initialization
Performance Considerations
Transform functions execute on every data refresh
Complex transformations may impact timeline performance
Consider data source
preloadsettings for dependent calculationsUse efficient algorithms for large datasets
The transform function provides powerful data manipulation capabilities while maintaining the flexibility to work with the existing render and grouping configurations.
Default Transform Function
The Timeline View includes a default transform function that handles segment ordering and end date assignment:
(dataSources) => {
const orderSegmentsAndAssigEndDate = (segments, paramsEndDate) => {
segments.sort((a, b) => {
const startA = new Date(a.startdate);
const startB = new Date(b.startdate);
return startA - startB;
});
const orderedSegments = segments.map((segment, index) => {
segment.index = index;
if (index === segments.length - 1) {
if (!segment.enddate) {
const allocationLastRpiDate = segment["allocation.vel_latestrpidate"];
const allocationEndDate = segment["allocation.vel_allocationenddate"];
segment.enddate =
allocationLastRpiDate || allocationEndDate || paramsEndDate;
}
return segment;
}
const nextSegment = segments[index + 1];
const enddate = new Date(nextSegment.startdate);
segment.enddate = enddate.toISOString();
const splitDate = segment.enddate.split("T")[0].split("-");
segment["enddate@OData.Community.Display.V1.FormattedValue"] =
splitDate[2] + "/" + splitDate[1] + "/" + splitDate[0];
return segment;
});
return orderedSegments;
};
let endDate;
const groupedSegments = dataSources.reduce((acc, dataSource) => {
endDate = dataSource.params?.endDate;
const segments = dataSource.entities || [];
segments.forEach((segment) => {
const allocation =
segment["_vel_rosterpositionallocationid_value"] || "default";
if (!acc[allocation]) {
acc[allocation] = [];
}
acc[allocation].push(segment);
});
return acc;
}, {});
const orderedSegments = Object.entries(groupedSegments).reduce(
(acc, [, segments]) =>
acc.concat(orderSegmentsAndAssigEndDate(segments, endDate)),
[]
);
return orderedSegments;
}
Default Transform Function Breakdown
Main Purpose: This function processes roster segments to ensure proper timeline display by grouping segments by allocation and calculating missing end dates.
Key Operations:
Data Grouping:
Groups segments by roster position allocation ID (
_vel_rosterpositionallocationid_value)Creates separate arrays for each allocation to process independently
Uses "default" as fallback group for segments without allocation
Chronological Ordering:
Sorts segments within each group by start date (
startdate)Ensures timeline items display in correct temporal sequence
Adds an
indexproperty to track segment position
End Date Calculation:
For intermediate segments: Uses the next segment's start date as the current segment's end date
For final segments: Uses a priority fallback system:
allocation.vel_latestrpidate- Latest RPI (Roster Pattern Instance) dateallocation.vel_allocationenddate- Allocation's official end dateparamsEndDate- Parameter end date from data source
Date Formatting:
Converts calculated end dates to ISO string format
Creates formatted display values in DD/MM/YYYY format
Ensures consistency with Power Platform's OData formatting conventions
Data Flow: 1. Extract entities from all data sources 2. Group segments by allocation ID 3. For each allocation group: - Sort segments chronologically - Calculate missing end dates using next segment's start or fallback values - Format dates for display 4. Flatten all groups back into a single array 5. Return processed segments for timeline rendering
Business Logic: - Handles continuous allocation periods where segments connect end-to-end - Ensures no gaps appear in timeline visualization - Provides intelligent end date inference for incomplete data - Maintains data integrity while enhancing display quality
This default transform function is specifically designed for roster management scenarios where allocation segments need to display as continuous periods without gaps, even when source data doesn't include explicit end dates for all segments.