Skip to main content

UI Views

Users can create custom planning views for different sub-systems (e.g. science, engineering, thermal, etc.), where only data (e.g. activities and resources) for those sub-systems are visualized. This is done through custom JSON configuration files (or directly via the UI). The format of a UI View is the subject of this document.

See the UI view JSON schema specification for the complete set of view object properties and types.

View Schema

This is the main type interface for the planning UI view:

type ViewActivityTable = {
columnDefs: ColDef[];
columnStates: ColumnState[];
id: number;
};

type ViewIFrame = {
id: number;
src: string;
title: string;
};

type ViewDefinition = {
plan: {
activityTables: ViewActivityTable[];
iFrames: ViewIFrame[];
layout: Grid;
timelines: Timeline[];
};
};

For example, here is a JSON object that implements an empty ViewDefinition interface:

{
"plan": {
"activityTables": [],
"iFrames": [],
"layout": {},
"timelines": []
}
}

Layout (Grid)

A planning UI view consists of a layout which describes the different visible components and how they are arranged in re-sizeable rows and columns. The layout follows the following Grid type definitions:

type GridComponent = {
activityTableId?: number;
componentName: string;
gridName?: string;
iFrameId?: number;
id: number;
props?: any;
timelineId?: number;
type: 'component';
};

type GridColumns = {
columnSizes: string;
columns: Grid[];
gridName?: string;
id: number;
type: 'columns';
};

type GridGutter = {
gridName?: string;
id: number;
track: number;
type: 'gutter';
};

type GridRows = {
gridName?: string;
id: number;
rowSizes: string;
rows: Grid[];
type: 'rows';
};

type Grid = GridColumns | GridComponent | GridGutter | GridRows;

For example, here is a grid layout definition in JSON:

{
"columnSizes": "1fr 3px 2fr 3px 1fr",
"columns": [
{ "componentName": "ActivityFormPanel", "id": 1, "type": "component" },
{ "id": 2, "track": 1, "type": "gutter" },
{
"id": 3,
"rowSizes": "70% 3px 1fr",
"rows": [
{
"componentName": "TimelinePanel",
"id": 4,
"timelineId": 0,
"type": "component"
},
{ "id": 5, "track": 1, "type": "gutter" },
{
"activityTableId": 0,
"componentName": "ActivityTablePanel",
"id": 6,
"type": "component"
}
],
"type": "rows"
},
{ "id": 7, "track": 3, "type": "gutter" },
{ "componentName": "ActivityTypesPanel", "id": 8, "type": "component" }
],
"gridName": "Activities",
"id": 0,
"type": "columns"
}

Timeline

The timelines section allows you to specify a list of timeline visualizations which display time-ordered data (i.e. activities or resources). Here is the interface of a timeline:

interface Timeline {
id: number;
marginLeft: number;
marginRight: number;
rows: Row[];
verticalGuides: VerticalGuide[];
}

To visualize data in a timeline you need to add row objects to the rows array. A row is a layered visualization of time-ordered data. Each layer of a row is specified as an object of the layers array. The interfaces for a Row, Layer, and ActivityOptions are as follows:

interface Row {
activityOptions?: ActivityOptions;
autoAdjustHeight: boolean;
expanded: boolean;
height: number;
horizontalGuides: HorizontalGuide[];
id: number;
layers: Layer[];
name: string;
yAxes: Axis[];
}

interface Layer {
chartType: 'activity' | 'line' | 'x-range';
filter: {
activity?: ActivityLayerFilter;
resource?: ResourceLayerFilter;
};
id: number;
yAxisId: number | null;
}

type ActivityOptions = {
// Height of activity subrows
activityHeight: number;

// Whether or not to display only directives, only spans, or both in the row
composition: 'directives' | 'spans' | 'both';

// Describes the primary method in which activities are visualized within this row
displayMode: 'grouped' | 'compact';

// If 'directive' the activities are grouped starting with directive types, if 'flat' activities are grouped by type regardless of hierarchy
hierarchyMode: 'directive' | 'flat';

// Activity text label behavior
labelVisibility: 'on' | 'off' | 'auto';
};

Here is a JSON object that creates a single row with one activity layer.

{
"autoAdjustHeight": true,
"height": 200,
"horizontalGuides": [],
"id": 0,
"layers": [
{
"activityColor": "#283593",
"activityHeight": 20,
"chartType": "activity",
"filter": { "activity": { "types": ["BiteBanana", "PickBanana"] } },
"id": 0,
"yAxisId": null
}
],
"yAxes": []
}

For data that has y-values (for example resource data), you can specify a y-axis and link a layer to it by ID. Here are the interfaces for Axis and Label:

interface Axis {
color: string;
id: number;
label: Label;
scaleDomain: (number | null)[];
tickCount: number | null;
}

interface Label {
color?: string;
text: string;
}

Y-axes are specified in the row separately from layers so we can specify multi-way relationships between axes and layers. For example you could have many layers corresponding to a single row axis.

Here is the JSON for creating a row with two overlaid resource layers. The first layer shows only resources with the name peel, and uses the y-axis with ID 1. The second layer shows only resources with the name fruit, and uses the y-axis with the ID 2.

{
"activityOptions": {
"activityHeight": 16,
"composition": "both",
"displayMode": "grouped",
"hierarchyMode": "flat",
"labelVisibility": "auto"
},
"autoAdjustHeight": false,
"height": 100,
"horizontalGuides": [],
"id": 1,
"layers": [
{
"chartType": "line",
"filter": { "resource": { "names": ["peel"] } },
"id": 1,
"lineColor": "#283593",
"lineWidth": 1,
"pointRadius": 2,
"yAxisId": 1
},
{
"chartType": "line",
"filter": { "resource": { "names": ["fruit"] } },
"id": 2,
"lineColor": "#ffcd69",
"lineWidth": 1,
"pointRadius": 2,
"yAxisId": 2
}
],
"yAxes": [
{
"color": "#000000",
"id": 1,
"label": { "text": "peel" },
"scaleDomain": [0, 4],
"tickCount": 5
},
{
"color": "#000000",
"id": 2,
"label": { "text": "fruit" },
"scaleDomain": [-10, 4],
"tickCount": 5
}
]
}

Activity Tables

Here is an example activityTables view JSON definition. The columnDefs and columnStates follow the schemas of the ag-grid ColDef and ColumnState respectively.

{
"activityTables": [
{
"columnDefs": [
{
"field": "id",
"filter": "agTextColumnFilter",
"headerName": "ID",
"sortable": true,
"resizable": true
},
{
"field": "type",
"filter": "agTextColumnFilter",
"headerName": "Type",
"sortable": true,
"resizable": true
},
{
"field": "start_time",
"filter": "agTextColumnFilter",
"headerName": "Start Time",
"sortable": true,
"resizable": true
},
{
"field": "duration",
"filter": "agTextColumnFilter",
"headerName": "Duration",
"sortable": true,
"resizable": true
}
],
"columnStates": [],
"id": 0
}
]
}

To use the ActivityTablePanel you need to add an ActivityTablePanel component to the grid layout and connect it via the activityTableId. For example:

{
"activityTableId": 0,
"componentName": "ActivityTablePanel",
"id": 2,
"type": "component"
}

Notice how we connect the grid component activityTableId with the id of the definition in the activityTables array.

IFrames

An IFrame component allows you to embed another application in the Aerie UI. Here is an example iFrames view JSON definition:

{
"iFrames": [
{
"id": 0,
"src": "https://eyes.nasa.gov/apps/solar-system",
"title": "NASA-Eyes-Solar-System"
}
]
}

To use the IFrame you need to add an IFramePanel component to the grid layout and connect it via the iFrameId. For example:

{ "componentName": "IFramePanel", "iFrameId": 0, "id": 1, "type": "component" }

Notice how we connect the grid component iFrameId with the id of the definition in the iFrames array.

GraphQL Queries

The following GraphQL queries can be used to programmatically operate on UI views.

Create Single View

mutation {
insert_view_one(
object: {
definition: { plan: { activityTables: [], iFrames: [], layout: {}, timelines: [] } }
name: "My First View"
owner: "system"
}
) {
id
}
}

Create Multiple Views

mutation {
insert_view(
objects: [
{
definition: { plan: { activityTables: [], iFrames: [], layout: {}, timelines: [] } }
name: "First View"
owner: "system"
}
{
definition: { plan: { activityTables: [], iFrames: [], layout: {}, timelines: [] } }
name: "Second View"
owner: "system"
}
]
) {
returning {
id
}
}
}

Get All Views

query {
view {
created_at
definition
id
name
owner
updated_at
}
}

Get Single View by ID

query {
view_by_pk(id: 1) {
created_at
definition
id
name
owner
updated_at
}
}

Delete Single View by ID

mutation {
delete_view_by_pk(id: 1) {
id
}
}

Delete All Views in the Database

mutation {
delete_view(where: {}) {
affected_rows
}
}

Update Definition and Name of a Single View by ID

mutation {
update_view_by_pk(pk_columns: { id: 1 }, _set: { definition: { plan: {} }, name: "New Name" }) {
id
}
}