Hi Team,
We have a requirement from the regarding a read-only grid. The grid is sorted first by Meeting Name, and then by Supplier Name. The client expects a visual indicator (like a bottom line or separator) between different suppliers to help users easily identify when the data switches from one supplier to another. For example, after all rows for Supplier A, there should be a highlighted row or separator before rows for Supplier B begin. Is this kind of visual separation possible in Appian grids?
Discussion posts and replies are publicly visible
You can achieve this with multiple approach i tried 2 approach.Let's have a look at it.
a!localVariables( /* Sample data matching your structure */ local!data: { a!map(meetingName: "Meeting A", supplierName: "Supplier A", data: 23), a!map(meetingName: "Meeting A", supplierName: "Supplier A", data: 87), a!map(meetingName: "Meeting A", supplierName: "Supplier B", data: 56), a!map(meetingName: "Meeting A", supplierName: "Supplier B", data: 7), a!map(meetingName: "Meeting B", supplierName: "Supplier A", data: 45), a!map(meetingName: "Meeting B", supplierName: "Supplier A", data: 456), a!map(meetingName: "Meeting B", supplierName: "Supplier B", data: 56), a!map(meetingName: "Meeting B", supplierName: "Supplier B", data: 56) }, /* Sort data by Meeting Name, then Supplier Name */ local!sortedData: todatasubset( local!data, a!pagingInfo( startIndex: 1, batchSize: -1, sort: { a!sortInfo(field: "meetingName", ascending: true), a!sortInfo(field: "supplierName", ascending: true) } ) ).data, /* Add index and previous supplier info to each row */ local!dataWithIndex: a!forEach( items: local!sortedData, expression: a!map( meetingName: fv!item.meetingName, supplierName: fv!item.supplierName, data: fv!item.data, index: fv!index, isNewSupplier: or( fv!index = 1, fv!item.supplierName <> index(local!sortedData, fv!index - 1, {}).supplierName ) ) ), { a!sectionLayout( label: "", contents: { /* Approach 1: Using Horizontal Line Separators */ a!cardLayout( contents: { a!richTextDisplayField( label: "Approach 1: Line Separators", labelPosition: "ABOVE", value:{} ), a!gridField( label: "", labelPosition: "COLLAPSED", data: local!dataWithIndex, columns: { a!gridColumn( label: "Meeting Name", value: a!richTextDisplayField( value: { if( and( fv!row.index > 1, fv!row.isNewSupplier ), { a!richTextItem( text: "────────────────────", color: "SECONDARY", size: "SMALL" ), a!richTextItem( text: char(10), size: "SMALL" ) }, {} ), a!richTextItem( text: fv!row.meetingName, style: if( fv!row.isNewSupplier, "STRONG", "PLAIN" ) ) } ), width: "AUTO" ), a!gridColumn( label: "Supplier Name", value: a!richTextDisplayField( value: { if( and( fv!row.index > 1, fv!row.isNewSupplier ), { a!richTextItem( text: "────────────────────", color: "SECONDARY", size: "SMALL" ), a!richTextItem( text: char(10), size: "SMALL" ) }, {} ), if( fv!row.isNewSupplier, { a!richTextItem( text: "▼ ", color: "ACCENT", size: "MEDIUM" ), a!richTextItem( text: fv!row.supplierName, style: "STRONG", color: "ACCENT" ) }, a!richTextItem( text: " " & fv!row.supplierName, size: "STANDARD" ) ) } ), width: "AUTO" ), a!gridColumn( label: "Data", value: a!richTextDisplayField( value: { if( and( fv!row.index > 1, fv!row.isNewSupplier ), { a!richTextItem( text: "────────", color: "SECONDARY", size: "SMALL" ), a!richTextItem( text: char(10), size: "SMALL" ) }, {} ), a!richTextItem( text: fv!row.data, style: if( fv!row.isNewSupplier, "STRONG", "PLAIN" ) ) } ), width: "AUTO", align: "END" ) }, rowHeader: 1, height: "AUTO", borderStyle: "LIGHT", shadeAlternateRows: false, spacing: "DENSE" ) }, style: "NONE", padding: "STANDARD", marginBelow: "STANDARD" ), /* Approach 2: Using Colored Text and Icons */ a!cardLayout( contents: { a!richTextDisplayField( label: "Approach 2: Colored Indicators", labelPosition: "ABOVE", value: {} ), a!gridField( label: "", labelPosition: "COLLAPSED", data: local!dataWithIndex, columns: { a!gridColumn( label: "", value: a!richTextDisplayField( value: { a!richTextItem( text: if( fv!row.isNewSupplier, "●", "" ), color: "POSITIVE", size: "LARGE", style: "STRONG" ) } ), width: "ICON" ), a!gridColumn( label: "Meeting Name", value: a!richTextDisplayField( value: a!richTextItem( text: fv!row.meetingName, style: if( fv!row.isNewSupplier, "STRONG", "PLAIN" ), size: if( fv!row.isNewSupplier, "MEDIUM", "STANDARD" ) ) ), width: "AUTO" ), a!gridColumn( label: "Supplier Name", value: a!richTextDisplayField( value: { if( fv!row.isNewSupplier, { a!richTextItem( text: "► ", color: "POSITIVE", style: "STRONG", size: "MEDIUM" ), a!richTextItem( text: fv!row.supplierName, style: "STRONG", color: "POSITIVE", size: "MEDIUM" ) }, a!richTextItem( text: fv!row.supplierName, color: "SECONDARY" ) ) } ), width: "AUTO" ), a!gridColumn( label: "Data", value: a!richTextDisplayField( value: a!richTextItem( text: fv!row.data, style: if( fv!row.isNewSupplier, "STRONG", "PLAIN" ), size: if( fv!row.isNewSupplier, "MEDIUM", "STANDARD" ) ) ), width: "AUTO", align: "END" ) }, rowHeader: 1, height: "AUTO", borderStyle: "LIGHT", shadeAlternateRows: true, spacing: "DENSE" ) }, style: "NONE", padding: "STANDARD", marginBelow: "STANDARD" ), } ) } )