View this page in the latest version of Appian. KPI Patterns Share Share via LinkedIn Reddit Email Copy Link Print On This Page Tip: Interface patterns give you an opportunity to explore different interface designs. Be sure to check out How to Adapt a Pattern for Your Application. Goal The Key Performance Indicator (KPI) patterns provide a common style and format for displaying important performance measures. This page explains how you can use this pattern in your interface, and walks through the design structure in detail. There are five KPI patterns to choose from. KPI patterns 1, 2, and 3 have the same functionality. KPI patterns 4 and 5 are slightly different, with 4 showing line charts and 5 containing a single card. Test out all five to see which one best matches your needs. The KPIs reflect how you can best use a single query to share related data across multiple components. The data used in the KPIs will usually be aggregated or sourced from a database view. Note: KPIs can now be built directly from your record data with the KPI component. This component offers quick configuration to add important metrics to your apps and help users make decisions based on business data. See the examples in the SAIL Design System. Design structure The main components in KPIs patterns 1-3 are card layouts, rich text display fields, and column layouts. In this design structure breakdown, we'll use KPI pattern 2 to explain how each component is used. You can drag and drop KPI 1 and 3 to see the same design, but with different colors and slightly different expression patterns. The image below displays how KPI 2 looks on a blank interface with callouts for the main components. You can examine the entire expression or jump down to the subsections below with referenced line numbers to see a detailed breakdown of the main components. Pattern expression When you drag and drop the KPI 2 pattern onto your interface, 233 lines of expression will be added to the section where you dragged it. 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 { a!localVariables( /* Creates an array of opportunities (opps) */ /* This would typically be returned with a query */ local!opportunities: { a!map(id: 1, accountExecId: round(rand() * 5, 0), amount: (round(rand(), 1) * 150000) + 90000, stage: tointeger(rand() * 2 + 1) ), a!map(id: 2, accountExecId: round(rand() * 5, 0), amount: (round(rand(), 1) * 150000) + 90000, stage: tointeger(rand() * 2 + 1) ), a!map(id: 3, accountExecId: round(rand() * 5, 0), amount: (round(rand(), 1) * 150000) + 90000, stage: tointeger(rand() * 2 + 1) ), a!map(id: 4, accountExecId: round(rand() * 5, 0), amount: (round(rand(), 1) * 150000) + 90000, stage: tointeger(rand() * 2 + 1) ), a!map(id: 5, accountExecId: round(rand() * 5, 0), amount: (round(rand(), 1) * 150000) + 90000, stage: tointeger(rand() * 2 + 1) ) }, /* Set targets for the KPIs */ local!oppsTarget: 5, local!stageTarget: 2, local!dealSizeTarget: 155000, local!pipelineTarget: 800000, { a!columnsLayout( columns: { a!columnLayout( contents: { a!columnsLayout( columns: { a!columnLayout( contents: { a!cardLayout( contents: { a!richTextDisplayField( labelPosition: "COLLAPSED", value: { a!richTextItem(text: {"TOTAL ACTIVE OPPS"}, color: "SECONDARY", style: "STRONG") } ), a!sideBySideLayout( items: { a!sideBySideItem( item: a!richTextDisplayField( labelPosition: "COLLAPSED", value: { /* Returns a count of all the active opportunities */ a!richTextItem( text: {count(local!opportunities)}, /* This changes the color of the text depending on whether or not the target was hit */ color: if(length(local!opportunities) >= local!oppsTarget, "POSITIVE", "NEGATIVE"), size: "LARGE" ), " ", a!richTextIcon(icon: "handshake-o", size: "LARGE") } ) ), a!sideBySideItem( item: a!richTextDisplayField( labelPosition: "COLLAPSED", value: { a!richTextIcon(icon: "bullseye", color: "SECONDARY"), " ", a!richTextItem(text: {local!oppsTarget}, color: "SECONDARY") }, align: "RIGHT" ), width: "MINIMIZE" ) }, alignVertical: "BOTTOM" ) } ) } ), a!columnLayout( contents: { a!cardLayout( contents: { a!richTextDisplayField( labelPosition: "COLLAPSED", value: { a!richTextItem(text: {"AVG DEAL SIZE"}, color: "SECONDARY", style: "STRONG") } ), a!sideBySideLayout( items: { a!sideBySideItem( item: a!richTextDisplayField( labelPosition: "COLLAPSED", value: { /* Returns the average dollar amount for the opportunities */ a!richTextItem( text: {a!currency(isoCode: "USD", value: average(local!opportunities.amount) / 1000, decimalPlaces: 0, format: "SYMBOL"), "K"}, /* This changes the color of the text depending on whether or not the target was hit */ color: if(average(local!opportunities.amount) >= local!dealSizeTarget, "POSITIVE", "NEGATIVE"), size: "LARGE" ), " ", a!richTextIcon(icon: "money", size: "LARGE") } ) ), a!sideBySideItem( item: a!richTextDisplayField( labelPosition: "COLLAPSED", value: { a!richTextIcon(icon: "bullseye", color: "SECONDARY"), " ", a!richTextItem(text: {a!currency(isoCode: "USD", value: local!dealSizeTarget / 1000, decimalPlaces: 0, format: "SYMBOL"), "K"}, color: "SECONDARY") }, align: "RIGHT" ), width: "MINIMIZE" ) }, alignVertical: "BOTTOM" ) } ) } ) } ) } ), a!columnLayout( contents: { a!columnsLayout( columns: { a!columnLayout( contents: { a!cardLayout( contents: { a!richTextDisplayField( labelPosition: "COLLAPSED", value: { a!richTextItem(text: {"TOTAL PIPELINE"}, color: "SECONDARY", style: "STRONG") } ), a!sideBySideLayout( items: { a!sideBySideItem( item: a!richTextDisplayField( labelPosition: "COLLAPSED", value: { /* Returns a total dollar amount for all opportunities */ a!richTextItem( text: {a!currency(isoCode: "USD", value: sum(local!opportunities.amount) / 1000, decimalPlaces: 0, format: "SYMBOL"), "K"}, /* This changes the color of the text depending on whether or not the target was hit */ color: if(sum(local!opportunities.amount) >= local!pipelineTarget, "POSITIVE", "NEGATIVE"), size: "LARGE" ), " ", a!richTextIcon(icon: "line-chart", size: "LARGE") } ) ), a!sideBySideItem( item: a!richTextDisplayField( labelPosition: "COLLAPSED", value: { a!richTextIcon(icon: "bullseye", color: "SECONDARY"), " ", a!richTextItem(text: {a!currency(isoCode: "USD", value: local!pipelineTarget / 1000, decimalPlaces: 0, format: "SYMBOL"), "K"}, color: "SECONDARY") }, align: "RIGHT" ), width: "MINIMIZE" ) }, alignVertical: "BOTTOM" ) } ) } ), a!columnLayout( contents: { a!cardLayout( contents: { a!richTextDisplayField( labelPosition: "COLLAPSED", value: { a!richTextItem(text: {"TOTAL STAGE 3 OPPS"}, color: "SECONDARY", style: "STRONG") } ), a!sideBySideLayout( items: { a!sideBySideItem( item: a!richTextDisplayField( labelPosition: "COLLAPSED", value: { /* Returns a count of all the active opportunities in stage 3 */ a!richTextItem( text: {count(local!opportunities[where(local!opportunities.stage = {3})])}, /* This changes the color of the text depending on whether or not the target was hit */ color: if(count(local!opportunities[where(local!opportunities.stage = {3})]) >= local!stageTarget, "POSITIVE", "NEGATIVE"), size: "LARGE" ), " ", a!richTextIcon(icon: "flag-checkered", size: "LARGE") } ) ), a!sideBySideItem( item: a!richTextDisplayField( labelPosition: "COLLAPSED", value: { a!richTextIcon(icon: "bullseye", color: "SECONDARY"), " ", a!richTextItem(text: {local!stageTarget}, color: "SECONDARY") }, align: "RIGHT" ), width: "MINIMIZE" ) }, alignVertical: "BOTTOM" ) } ) } ) } ) } ) }, stackWhen: { "PHONE", "TABLET_PORTRAIT", "TABLET_LANDSCAPE" } ) } ) } [Line 1-16] Define opportunities and targets At the top of the pattern, local variables set up the data that will be used in the cards. 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 { a!localVariables( /* Creates an array of opportunities (opps) */ /* This would typically be returned with a query */ local!opportunities: { a!map(id: 1, accountExecId: round(rand() * 5, 0), amount: (round(rand(), 1) * 150000) + 90000, stage: tointeger(rand() * 2 + 1) ), a!map(id: 2, accountExecId: round(rand() * 5, 0), amount: (round(rand(), 1) * 150000) + 90000, stage: tointeger(rand() * 2 + 1) ), a!map(id: 3, accountExecId: round(rand() * 5, 0), amount: (round(rand(), 1) * 150000) + 90000, stage: tointeger(rand() * 2 + 1) ), a!map(id: 4, accountExecId: round(rand() * 5, 0), amount: (round(rand(), 1) * 150000) + 90000, stage: tointeger(rand() * 2 + 1) ), a!map(id: 5, accountExecId: round(rand() * 5, 0), amount: (round(rand(), 1) * 150000) + 90000, stage: tointeger(rand() * 2 + 1) ) }, /* Set targets for the KPIs */ local!oppsTarget: 5, local!stageTarget: 2, local!dealSizeTarget: 155000, local!pipelineTarget: 800000, [Line 17-70] Use a card layout in a column layout to display KPIs The first visible component is a card layout in a column layout with the label, "TOTAL ACTIVE OPPS", in a rich text display field. For this particular KPI, we use a count() statement to display a count of the total active opportunities. We then use another rich text item to display the comparison between active opportunities and the opportunity target. We also use an if() statement to change the color of the rich text depending on whether the opportunity target was hit or not. This kind of conditional styling based on the data being displayed is recommended to make it easy for the viewer to quickly understand the data in each KPI. 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 { a!columnsLayout( columns: { a!columnLayout( contents: { a!columnsLayout( columns: { a!columnLayout( contents: { a!cardLayout( contents: { a!richTextDisplayField( labelPosition: "COLLAPSED", value: { a!richTextItem(text: {"TOTAL ACTIVE OPPS"}, color: "SECONDARY", style: "STRONG") } ), a!sideBySideLayout( items: { a!sideBySideItem( item: a!richTextDisplayField( labelPosition: "COLLAPSED", value: { /* Returns a count of all the active opportunities */ a!richTextItem( text: {count(local!opportunities)}, /* This changes the color of the text depending on whether or not the target was hit */ color: if(length(local!opportunities) >= local!oppsTarget, "POSITIVE", "NEGATIVE"), size: "LARGE" ), " ", a!richTextIcon(icon: "handshake-o", size: "LARGE") } ) ), a!sideBySideItem( item: a!richTextDisplayField( labelPosition: "COLLAPSED", value: { a!richTextIcon(icon: "bullseye", color: "SECONDARY"), " ", a!richTextItem(text: {local!oppsTarget}, color: "SECONDARY") }, align: "RIGHT" ), width: "MINIMIZE" ) }, alignVertical: "BOTTOM" ) } ) } ), This pattern's other three KPI cards (lines 71 through 233) follow the same structure as the first to determine the display. Feedback Was this page helpful? SHARE FEEDBACK Loading...