Record Browser
Browse and inspect records with sidebar nav, live search and category chip filters, and a Grid / List / Table view switcher. Selecting a record opens a detail panel with a live stock card and an Activity Timeline of its history — a bottom sheet on mobile, with bottom nav and a FAB.
Two parts to paste. This template needs both the Screen YAML and the App.Formulas — without the formulas the screen renders blank. Copy both below, or just download the .msapp to skip the paste.
Preview
Interactive preview. The actual template runs natively in Power Apps.
Included in this template
Live search
Filter by title, ID, or assignee
Category chips
One-tap category filter strip
View switcher
Grid / List / Table via segmented control
Stock-level bar
Data Table progress bar bucketed 0–5
Detail panel
Slide-in record detail on desktop
Activity timeline
Full stock-movement history per record
Prev / next
Step through the filtered list in the panel
Mobile sheet + FAB
Bottom sheet detail, bottom nav, and speed-dial
Components Used
This template uses these components. Install them before importing the screen YAML.
cmpDataTable
AISortable rows with a segmented stock-level bar and row select
cmpChips
AICategory filter strip with active state
cmpSegmentedControl
AIGrid / List / Table view switcher
cmpActivityTimeline
AIRecord history in the detail panel and mobile sheet
cmpSidebar
Collapsible left navigation rail
cmpBottomNavigation
Mobile tab bar
cmpFAB
Mobile speed-dial for quick actions
Code
Screens:
scrRecordBrowser:
Properties:
Fill: =RGBA(240, 242, 247, 1)
LoadingSpinnerColor: =RGBA(37, 99, 235, 1)
OnVisible: |-
=// ============================================================
// Record Browser — demo data + screen state
// Seeding is gated on gblRbSeedVersion, not IsEmpty, so a session
// still holding an older column shape is reseeded rather than
// silently keeping it. Bump the token when a shape changes.
// Swap each ClearCollect for a Filter against your real source.
// ============================================================
If(
gblRbSeedVersion <> "2026-07-27",
ClearCollect(colRbItems,
{ItemID:1, CaseID:"ELC-0042", Title:"HDMI Cable 2m", Status:"Critical", StatusRank:1, Progress:"P1", Assignee:"Warehouse A", DueDate:Date(2026,3,18), Priority:"Electronics", Category:"Electronics", OnHand:3, Reorder:25, UnitCost:0.6, TotalValue:1.8, LeadTime:"7-10 days"},
{ItemID:2, CaseID:"SAF-0118", Title:"Safety Vest (M)", Status:"Critical", StatusRank:1, Progress:"P2", Assignee:"Warehouse B", DueDate:Date(2026,3,17), Priority:"Safety", Category:"Safety", OnHand:5, Reorder:30, UnitCost:2.5, TotalValue:12.5, LeadTime:"5-7 days"},
{ItemID:3, CaseID:"PKG-0237", Title:"Label Printer Ribbon", Status:"Critical", StatusRank:1, Progress:"P3", Assignee:"Warehouse A", DueDate:Date(2026,3,16), Priority:"Packaging", Category:"Packaging", OnHand:8, Reorder:40, UnitCost:0.5, TotalValue:4, LeadTime:"3-5 days"},
{ItemID:4, CaseID:"ELC-0091", Title:"AA Batteries (48-pack)", Status:"Critical", StatusRank:1, Progress:"P4", Assignee:"Warehouse C", DueDate:Date(2026,3,15), Priority:"Electronics", Category:"Electronics", OnHand:12, Reorder:50, UnitCost:5, TotalValue:60, LeadTime:"7-10 days"},
{ItemID:5, CaseID:"HWT-0055", Title:"Box Cutter Blades", Status:"Low Stock", StatusRank:2, Progress:"P5", Assignee:"Warehouse A", DueDate:Date(2026,3,14), Priority:"Hardware", Category:"Hardware", OnHand:18, Reorder:50, UnitCost:0.5, TotalValue:9, LeadTime:"4-6 days"},
{ItemID:6, CaseID:"PKG-0044", Title:"Stretch Wrap Roll", Status:"Low Stock", StatusRank:2, Progress:"P6", Assignee:"Warehouse B", DueDate:Date(2026,3,13), Priority:"Packaging", Category:"Packaging", OnHand:22, Reorder:60, UnitCost:4, TotalValue:88, LeadTime:"3-5 days"},
{ItemID:7, CaseID:"SAF-0203", Title:"Nitrile Gloves (L)", Status:"Low Stock", StatusRank:2, Progress:"P7", Assignee:"Warehouse D", DueDate:Date(2026,3,12), Priority:"Safety", Category:"Safety", OnHand:30, Reorder:75, UnitCost:2.5, TotalValue:75, LeadTime:"5-7 days"},
{ItemID:8, CaseID:"ELC-0188", Title:"Ethernet Cable 5m", Status:"Low Stock", StatusRank:2, Progress:"P8", Assignee:"Warehouse A", DueDate:Date(2026,3,11), Priority:"Electronics", Category:"Electronics", OnHand:41, Reorder:60, UnitCost:2, TotalValue:82, LeadTime:"7-10 days"},
{ItemID:9, CaseID:"HWT-0112", Title:"Drill Bit Set (10pc)", Status:"Active", StatusRank:3, Progress:"P9", Assignee:"Warehouse B", DueDate:Date(2026,3,10), Priority:"Hardware", Category:"Hardware", OnHand:84, Reorder:40, UnitCost:5, TotalValue:420, LeadTime:"4-6 days"},
{ItemID:10, CaseID:"CLN-0031", Title:"Antibacterial Wipes", Status:"Active", StatusRank:3, Progress:"P10", Assignee:"Warehouse C", DueDate:Date(2026,3,9), Priority:"Cleaning", Category:"Cleaning", OnHand:120, Reorder:80, UnitCost:2, TotalValue:240, LeadTime:"2-4 days"},
{ItemID:11, CaseID:"OFF-0077", Title:"Office Chair Mat", Status:"Active", StatusRank:3, Progress:"P11", Assignee:"Warehouse A", DueDate:Date(2026,3,8), Priority:"Office", Category:"Office", OnHand:14, Reorder:10, UnitCost:50, TotalValue:700, LeadTime:"10-14 days"},
{ItemID:12, CaseID:"SPR-0309", Title:"Bearing Kit 6200", Status:"Active", StatusRank:3, Progress:"P12", Assignee:"Warehouse D", DueDate:Date(2026,3,7), Priority:"Spare Parts", Category:"Spare Parts", OnHand:60, Reorder:30, UnitCost:5, TotalValue:300, LeadTime:"14-21 days"},
{ItemID:13, CaseID:"PKG-0091", Title:"Bubble Wrap (50m)", Status:"Low Stock", StatusRank:2, Progress:"P13", Assignee:"Warehouse B", DueDate:Date(2026,3,6), Priority:"Packaging", Category:"Packaging", OnHand:9, Reorder:20, UnitCost:4, TotalValue:36, LeadTime:"3-5 days"},
{ItemID:14, CaseID:"SAF-0044", Title:"Safety Goggles", Status:"Active", StatusRank:3, Progress:"P14", Assignee:"Warehouse A", DueDate:Date(2026,3,5), Priority:"Safety", Category:"Safety", OnHand:55, Reorder:25, UnitCost:5, TotalValue:275, LeadTime:"5-7 days"},
{ItemID:15, CaseID:"ELC-0301", Title:"USB-C Hub 7-Port", Status:"Active", StatusRank:3, Progress:"P15", Assignee:"Warehouse C", DueDate:Date(2026,3,4), Priority:"Electronics", Category:"Electronics", OnHand:28, Reorder:20, UnitCost:30, TotalValue:840, LeadTime:"7-10 days"},
{ItemID:16, CaseID:"HWT-0088", Title:"Cable Ties (100pk)", Status:"Active", StatusRank:3, Progress:"P16", Assignee:"Warehouse A", DueDate:Date(2026,3,3), Priority:"Hardware", Category:"Hardware", OnHand:200, Reorder:100, UnitCost:1, TotalValue:200, LeadTime:"4-6 days"},
{ItemID:17, CaseID:"CLN-0055", Title:"Floor Cleaner 5L", Status:"Active", StatusRank:3, Progress:"P17", Assignee:"Warehouse B", DueDate:Date(2026,3,2), Priority:"Cleaning", Category:"Cleaning", OnHand:42, Reorder:30, UnitCost:6, TotalValue:252, LeadTime:"2-4 days"},
{ItemID:18, CaseID:"OFF-0112", Title:"Laser Printer Paper", Status:"Inactive", StatusRank:4, Progress:"P18", Assignee:"Warehouse C", DueDate:Date(2026,3,1), Priority:"Office", Category:"Office", OnHand:0, Reorder:20, UnitCost:8, TotalValue:0, LeadTime:"10-14 days"}
);
// ---- category chips. label must match the Category column exactly ----
ClearCollect(colRbCats,
{id:"1", label:"All", icon:"", color:"", variant:"", selected:true},
{id:"2", label:"Electronics", icon:"", color:"", variant:"", selected:false},
{id:"3", label:"Hardware", icon:"", color:"", variant:"", selected:false},
{id:"4", label:"Safety", icon:"", color:"", variant:"", selected:false},
{id:"5", label:"Packaging", icon:"", color:"", variant:"", selected:false},
{id:"6", label:"Cleaning", icon:"", color:"", variant:"", selected:false},
{id:"7", label:"Office", icon:"", color:"", variant:"", selected:false},
{id:"8", label:"Spare Parts", icon:"", color:"", variant:"", selected:false}
);
// ---- sort options. Col must be a real column name on colRbItems ----
ClearCollect(colRbSortOptions,
{Value:"Name", Col:"Title"},
{Value:"Stock Level", Col:"OnHand"},
{Value:"Value", Col:"TotalValue"},
{Value:"Category", Col:"Category"},
{Value:"Status", Col:"StatusRank"}
);
// ---- movement history. CaseID ties each event to an item, which is
// what nfRbHistory filters on. Without it every record shows
// the same events. Not every item has history, by design, so
// the empty state gets exercised too. ----
ClearCollect(colRbHistory,
{CaseID:"ELC-0042", EventTitle:"Stock received", EventDescription:"PO-0041 · 50 units", Author:"Receiving", DisplayDateTime:"Mar 18, 2026 9:12 AM", Category:"Stock In", HasSubDetail:false, SubTo:"", SubCC:"", SubPriority:"", SubMessage:""},
{CaseID:"ELC-0042", EventTitle:"Consumed / dispatched", EventDescription:"WH-A · 12 units", Author:"Ops", DisplayDateTime:"Mar 15, 2026 2:40 PM", Category:"Stock Out", HasSubDetail:false, SubTo:"", SubCC:"", SubPriority:"", SubMessage:""},
{CaseID:"ELC-0042", EventTitle:"Manual adjustment", EventDescription:"Cycle count · -2 units", Author:"Admin", DisplayDateTime:"Mar 10, 2026 11:05 AM", Category:"Adjustment", HasSubDetail:false, SubTo:"", SubCC:"", SubPriority:"", SubMessage:""},
{CaseID:"SAF-0118", EventTitle:"Stock received", EventDescription:"PO-0036 · 40 units", Author:"Receiving", DisplayDateTime:"Feb 28, 2026 8:30 AM", Category:"Stock In", HasSubDetail:false, SubTo:"", SubCC:"", SubPriority:"", SubMessage:""},
{CaseID:"SAF-0118", EventTitle:"Consumed / dispatched", EventDescription:"WH-B · 25 units", Author:"Ops", DisplayDateTime:"Mar 12, 2026 9:05 AM", Category:"Stock Out", HasSubDetail:false, SubTo:"", SubCC:"", SubPriority:"", SubMessage:""},
{CaseID:"PKG-0237", EventTitle:"Stock received", EventDescription:"PO-0039 · 20 units", Author:"Receiving", DisplayDateTime:"Mar 05, 2026 10:40 AM", Category:"Stock In", HasSubDetail:false, SubTo:"", SubCC:"", SubPriority:"", SubMessage:""},
{CaseID:"HWT-0112", EventTitle:"Stock received", EventDescription:"PO-0044 · 100 units", Author:"Receiving", DisplayDateTime:"Mar 09, 2026 8:15 AM", Category:"Stock In", HasSubDetail:false, SubTo:"", SubCC:"", SubPriority:"", SubMessage:""},
{CaseID:"HWT-0112", EventTitle:"Manual adjustment", EventDescription:"Damaged · -6 units", Author:"Admin", DisplayDateTime:"Mar 11, 2026 3:20 PM", Category:"Adjustment", HasSubDetail:false, SubTo:"", SubCC:"", SubPriority:"", SubMessage:""}
);
// ---- category icon map. COLOR is substituted at eval time; never
// concatenate around the hex or Power Apps caches the first
// render of the data URI. ----
ClearCollect(colRbIcons,
{Category:"Electronics", Svg:"<svg viewBox='0 0 24 24' xmlns='http://www.w3.org/2000/svg' fill='none' stroke='COLOR' stroke-width='1.9' stroke-linecap='round' stroke-linejoin='round'><path d='M12 22v-5'/><path d='M9 8V2'/><path d='M15 8V2'/><path d='M18 8v3a6 6 0 0 1-12 0V8z'/></svg>"},
{Category:"Hardware", Svg:"<svg viewBox='0 0 24 24' xmlns='http://www.w3.org/2000/svg' fill='none' stroke='COLOR' stroke-width='1.9' stroke-linecap='round' stroke-linejoin='round'><path d='M14.7 6.3a1 1 0 0 0 0 1.4l1.6 1.6a1 1 0 0 0 1.4 0l3.77-3.77a6 6 0 0 1-7.94 7.94l-6.91 6.91a2.12 2.12 0 0 1-3-3l6.91-6.91a6 6 0 0 1 7.94-7.94l-3.76 3.76z'/></svg>"},
{Category:"Safety", Svg:"<svg viewBox='0 0 24 24' xmlns='http://www.w3.org/2000/svg' fill='none' stroke='COLOR' stroke-width='1.9' stroke-linecap='round' stroke-linejoin='round'><path d='M20 13c0 5-3.5 7.5-7.66 8.95a1 1 0 0 1-.67 0C7.5 20.5 4 18 4 13V6a1 1 0 0 1 1-1c2 0 4.5-1.2 6.24-2.72a1.17 1.17 0 0 1 1.52 0C14.51 3.81 17 5 19 5a1 1 0 0 1 1 1z'/><path d='m9 12 2 2 4-4'/></svg>"},
{Category:"Packaging", Svg:"<svg viewBox='0 0 24 24' xmlns='http://www.w3.org/2000/svg' fill='none' stroke='COLOR' stroke-width='1.9' stroke-linecap='round' stroke-linejoin='round'><path d='M21 16V8a2 2 0 0 0-1-1.73l-7-4a2 2 0 0 0-2 0l-7 4A2 2 0 0 0 3 8v8a2 2 0 0 0 1 1.73l7 4a2 2 0 0 0 2 0l7-4A2 2 0 0 0 21 16z'/><polyline points='3.29 7 12 12 20.71 7'/><line x1='12' y1='22' x2='12' y2='12'/></svg>"},
{Category:"Cleaning", Svg:"<svg viewBox='0 0 24 24' xmlns='http://www.w3.org/2000/svg' fill='none' stroke='COLOR' stroke-width='1.9' stroke-linecap='round' stroke-linejoin='round'><path d='M12 22a7 7 0 0 0 7-7c0-2-1-3.9-3-5.5s-3.5-4-4-6.5c-.5 2.5-2 4.9-4 6.5C6 11.1 5 13 5 15a7 7 0 0 0 7 7z'/></svg>"},
{Category:"Office", Svg:"<svg viewBox='0 0 24 24' xmlns='http://www.w3.org/2000/svg' fill='none' stroke='COLOR' stroke-width='1.9' stroke-linecap='round' stroke-linejoin='round'><path d='M14 2H6a2 2 0 0 0-2 2v16a2 2 0 0 0 2 2h12a2 2 0 0 0 2-2V8z'/><polyline points='14 2 14 8 20 8'/><line x1='16' y1='13' x2='8' y2='13'/><line x1='16' y1='17' x2='8' y2='17'/></svg>"},
{Category:"Spare Parts", Svg:"<svg viewBox='0 0 24 24' xmlns='http://www.w3.org/2000/svg' fill='none' stroke='COLOR' stroke-width='1.9' stroke-linecap='round' stroke-linejoin='round'><circle cx='12' cy='12' r='3'/><path d='M12 2v3'/><path d='M12 19v3'/><path d='M4.2 4.2l2.1 2.1'/><path d='M17.7 17.7l2.1 2.1'/><path d='M2 12h3'/><path d='M19 12h3'/><path d='M4.2 19.8l2.1-2.1'/><path d='M17.7 6.3l2.1-2.1'/></svg>"},
{Category:"default", Svg:"<svg viewBox='0 0 24 24' xmlns='http://www.w3.org/2000/svg' fill='none' stroke='COLOR' stroke-width='1.9' stroke-linecap='round' stroke-linejoin='round'><rect x='3' y='3' width='18' height='18' rx='2'/><path d='M3 9h18'/><path d='M9 21V9'/></svg>"}
);
Set(gblRbSeedVersion, "2026-07-27")
);
// ---- screen state ----
If(IsBlank(gblRbView), Set(gblRbView, "table"));
If(IsBlank(gblRbSortCol), Set(gblRbSortCol, "Title"));
If(IsBlank(gblRbSortDir), Set(gblRbSortDir, 1));
Set(gblRbSearch, "");
Set(gblRbCategory, "All");
Set(gblRbSelKey, "");
Set(gblRbActiveTab, 2);
Set(gblRbDeleteKey, "");
// ---- component-owned globals (contract, not renameable) ----
Set(_cmpNavIsExpanded, true);
Set(_cmpNavSelID, 2);
Set(_fabOpen, false);
Set(_auditFilter, Blank());
UpdateContext({locRbPanelOpen: false, locRbSheetOpen: false})
Children:
- cntRootRb:
Control: GroupContainer@1.5.0
Variant: AutoLayout
Properties:
DropShadow: =DropShadow.None
Fill: =RGBA(240, 242, 247, 1)
Height: =Parent.Height
LayoutDirection: =LayoutDirection.Vertical
PaddingLeft: =If(App.Width >= 1024, If(_cmpNavIsExpanded, 224, 52), 0)
Width: =Parent.Width
Children:
- cntMiddleRb:
Control: GroupContainer@1.5.0
Variant: AutoLayout
Properties:
DropShadow: =DropShadow.None
FillPortions: =0
Height: =Parent.Height
LayoutDirection: =LayoutDirection.Horizontal
Width: =Parent.Width
Children:
- cntRightStackRb:
Control: GroupContainer@1.5.0
Variant: AutoLayout
Properties:
DropShadow: =DropShadow.None
Height: =Parent.Height
LayoutAlignItems: =LayoutAlignItems.Stretch
LayoutDirection: =LayoutDirection.Vertical
LayoutMinHeight: =0
Children:
- conTopBarRb:
Control: GroupContainer@1.5.0
Variant: AutoLayout
Properties:
DropShadow: =DropShadow.None
Fill: =RGBA(255, 255, 255, 1)
FillPortions: =0
Height: =54
LayoutAlignItems: =LayoutAlignItems.Center
LayoutDirection: =LayoutDirection.Horizontal
LayoutGap: =12
LayoutJustifyContent: =LayoutJustifyContent.SpaceBetween
LayoutMinHeight: =0
PaddingLeft: =If(App.Width < 640, 14, 20)
PaddingRight: =If(App.Width < 640, 14, 20)
Children:
- conTbTitleRb:
Control: GroupContainer@1.5.0
Variant: AutoLayout
Properties:
DropShadow: =DropShadow.None
LayoutDirection: =LayoutDirection.Vertical
LayoutGap: =1
LayoutJustifyContent: =LayoutJustifyContent.Center
LayoutMinHeight: =0
LayoutMinWidth: =0
Children:
- lblRbTitle:
Control: ModernText@1.0.0
Properties:
AlignInContainer: =AlignInContainer.Stretch
AutoHeight: =true
Color: =RGBA(17, 24, 39, 1)
FontWeight: =FontWeight.Semibold
LayoutMinHeight: =0
LayoutMinWidth: =0
Text: ="All Items"
- lblRbSubtitle:
Control: ModernText@1.0.0
Properties:
AlignInContainer: =AlignInContainer.Stretch
AutoHeight: =true
Color: =RGBA(156, 163, 175, 1)
LayoutMinHeight: =0
LayoutMinWidth: =0
Size: =11
Text: |-
=Text(CountRows(colRbItems), "[$-en-US]#,###") & " records · " &
CountRows(Distinct(colRbItems, Assignee)) & " locations"
Visible: =App.Width >= 640
- conTbActionsRb:
Control: GroupContainer@1.5.0
Variant: AutoLayout
Properties:
DropShadow: =DropShadow.None
FillPortions: =0
LayoutAlignItems: =LayoutAlignItems.Center
LayoutDirection: =LayoutDirection.Horizontal
LayoutGap: =8
LayoutJustifyContent: =LayoutJustifyContent.End
LayoutMinHeight: =0
LayoutMinWidth: =0
Visible: =App.Width >= 640
Children:
- btnRbExport:
Control: Button@0.0.45
Properties:
Appearance: ='ButtonCanvas.Appearance'.Secondary
Icon: ="ArrowDownload"
LayoutMinHeight: =0
LayoutMinWidth: =0
OnSelect: =Notify("Export coming soon", NotificationType.Information)
Text: ="Export"
- btnRbAdd:
Control: Button@0.0.45
Properties:
Appearance: ='ButtonCanvas.Appearance'.Primary
Icon: ="Add"
LayoutMinHeight: =0
LayoutMinWidth: =0
OnSelect: =Notify("Add item coming soon", NotificationType.Information)
Text: ="Add Item"
Width: =140
- shpRbTopDivider:
Control: Rectangle@2.3.0
Properties:
AlignInContainer: =AlignInContainer.Stretch
BorderStyle: =BorderStyle.None
Fill: =RGBA(228, 232, 240, 1)
Height: =1
LayoutMinHeight: =1
- conFilterBarRb:
Control: GroupContainer@1.5.0
Variant: AutoLayout
Properties:
DropShadow: =DropShadow.None
Fill: =RGBA(255, 255, 255, 1)
FillPortions: =0
Height: =46
LayoutAlignItems: =LayoutAlignItems.Center
LayoutDirection: =LayoutDirection.Horizontal
LayoutGap: =10
LayoutMinHeight: =0
PaddingLeft: =If(App.Width < 640, 14, 20)
PaddingRight: =If(App.Width < 640, 14, 16)
Children:
- txtRbSearch:
Control: TextInput@0.0.54
Properties:
AlignInContainer: =AlignInContainer.Center
Fill: =RGBA(240, 242, 247, 1)
FontColor: =RGBA(17, 24, 39, 1)
FontSize: =12
Height: =30
LayoutMinHeight: =0
LayoutMinWidth: =0
OnChange: =Set(gblRbSearch, Self.Value)
Placeholder: ="Search items, SKU…"
TriggerOutput: ='TextInputCanvas.TriggerOutput'.Keypress
Type: ='TextInputCanvas.Type'.Search
Width: =If(App.Width < 640, 150, 220)
- shpRbFbDiv1:
Control: Rectangle@2.3.0
Properties:
AlignInContainer: =AlignInContainer.Center
BorderStyle: =BorderStyle.None
Fill: =RGBA(228, 232, 240, 1)
Height: =18
LayoutMinWidth: =1
Visible: =App.Width >= 768
Width: =1
- cmpChipsRb:
Control: CanvasComponent
ComponentName: cmpChips
Properties:
AlignInContainer: =AlignInContainer.Center
CharWidths: =
ChipType: ="choice"
Color: ="primary"
Config: |-
={Theme: "light", Wrap: false, Closable: false, Disabled: false, Radius: 99, Gap: 5, MaxVisible: 10}
CustomColors: |-
={primary: "", secondary: "", success: "", warning: "", error: ""}
FillPortions: =1
Height: =38
Icons: |-
={Prepend: "", Check: ""}
Items: =colRbCats
LayoutMinHeight: =0
LayoutMinWidth: =0
OnClose: =false
OnOverflow: =false
OnSelect: |-
=UpdateIf(colRbCats, true, {selected: false});
UpdateIf(colRbCats, id = cmpChipsRb.SelectedItem.id, {selected: true});
Set(gblRbCategory, cmpChipsRb.SelectedItem.label);
Set(gblRbSelKey, "");
UpdateContext({locRbPanelOpen: false, locRbSheetOpen: false})
Size: ="x-small"
Variant: ="tonal"
Visible: =App.Width >= 768
- shpRbFbDiv2:
Control: Rectangle@2.3.0
Properties:
AlignInContainer: =AlignInContainer.Center
BorderStyle: =BorderStyle.None
Fill: =RGBA(228, 232, 240, 1)
Height: =18
LayoutMinWidth: =1
Visible: =App.Width >= 768
Width: =1
- cmbRbSort:
Control: ModernCombobox@1.1.1
Properties:
AlignInContainer: =AlignInContainer.Center
Appearance: =Appearance.Outline
DefaultSelectedItems: =Filter(colRbSortOptions, Col = gblRbSortCol)
Height: =30
IsSearchable: =false
ItemDisplayText: =ThisItem.Value
Items: =colRbSortOptions
LayoutMinHeight: =0
LayoutMinWidth: =0
OnChange: =Set(gblRbSortCol, Self.Selected.Col)
SelectMultiple: =false
Size: =12
Visible: =App.Width >= 768
Width: =140
- btnRbSortDir:
Control: Button@0.0.45
Properties:
AlignInContainer: =AlignInContainer.Center
Appearance: ='ButtonCanvas.Appearance'.Secondary
Height: =30
Icon: =If(gblRbSortDir = 1, "ArrowUp", "ArrowDown")
LayoutMinHeight: =0
LayoutMinWidth: =0
OnSelect: =Set(gblRbSortDir, -gblRbSortDir)
Text: =""
Visible: =App.Width >= 768
Width: =34
- cmpViewSegRb:
Control: CanvasComponent
ComponentName: cmpSegmentedControl
Properties:
AlignInContainer: =AlignInContainer.Center
DefaultValue: ="table"
Disabled: =false
DisplayMode: ="Icons"
EqualWidth: =false
Height: =32
IconPosition: ="Left"
LayoutMinHeight: =0
LayoutMinWidth: =0
OnChange: =Set(gblRbView, cmpViewSegRb.SelectedValue)
Options: |-
=Table(
{
Value: "card",
Label: "Grid",
Icon: "data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='16' height='16' viewBox='0 0 24 24' fill='none' stroke='currentColor' stroke-width='2' stroke-linecap='round' stroke-linejoin='round'%3E%3Crect width='7' height='7' x='3' y='3' rx='1'/%3E%3Crect width='7' height='7' x='14' y='3' rx='1'/%3E%3Crect width='7' height='7' x='3' y='14' rx='1'/%3E%3Crect width='7' height='7' x='14' y='14' rx='1'/%3E%3C/svg%3E"
},
{
Value: "list",
Label: "List",
Icon: "data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='16' height='16' viewBox='0 0 24 24' fill='none' stroke='currentColor' stroke-width='2' stroke-linecap='round' stroke-linejoin='round'%3E%3Cline x1='8' x2='21' y1='6' y2='6'/%3E%3Cline x1='8' x2='21' y1='12' y2='12'/%3E%3Cline x1='8' x2='21' y1='18' y2='18'/%3E%3Cline x1='3' x2='3.01' y1='6' y2='6'/%3E%3Cline x1='3' x2='3.01' y1='12' y2='12'/%3E%3Cline x1='3' x2='3.01' y1='18' y2='18'/%3E%3C/svg%3E"
},
{
Value: "table",
Label: "Table",
Icon: "data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='16' height='16' viewBox='0 0 24 24' fill='none' stroke='currentColor' stroke-width='2' stroke-linecap='round' stroke-linejoin='round'%3E%3Cpath d='M12 3v18'/%3E%3Crect width='18' height='18' x='3' y='3' rx='2'/%3E%3Cpath d='M3 9h18'/%3E%3Cpath d='M3 15h18'/%3E%3C/svg%3E"
}
)
Size: ="Small"
Theme: ="Light"
Width: =120
- shpRbFilterDivider:
Control: Rectangle@2.3.0
Properties:
AlignInContainer: =AlignInContainer.Stretch
BorderStyle: =BorderStyle.None
Fill: =RGBA(228, 232, 240, 1)
Height: =1
LayoutMinHeight: =1
- conBodyRb:
Control: GroupContainer@1.5.0
Variant: AutoLayout
Properties:
DropShadow: =DropShadow.None
LayoutAlignItems: =LayoutAlignItems.Stretch
LayoutDirection: =LayoutDirection.Horizontal
LayoutMinHeight: =0
Children:
- conListAreaRb:
Control: GroupContainer@1.5.0
Variant: AutoLayout
Properties:
DropShadow: =DropShadow.None
LayoutDirection: =LayoutDirection.Vertical
LayoutMinHeight: =0
LayoutMinWidth: =0
PaddingBottom: =12
PaddingLeft: =If(App.Width < 640, 12, 16)
PaddingRight: =If(App.Width < 640, 12, 16)
PaddingTop: =12
Children:
- cmpMyTableRb:
Control: CanvasComponent
ComponentName: cmpMyTable
Properties:
AlignInContainer: =AlignInContainer.Stretch
CharWidths: =
ContextMenuItems: |-
=Table(
{ItemKey: "view", ItemDisplayName: "View details", ItemEnabled: true, ItemVisible: true},
{ItemKey: "createpo", ItemDisplayName: "Create PO", ItemEnabled: true, ItemVisible: true},
{ItemKey: "transfer", ItemDisplayName: "Transfer", ItemEnabled: true, ItemVisible: true},
{ItemKey: "delete", ItemDisplayName: "Delete", ItemEnabled: true, ItemVisible: true}
)
EnableHover: =true
EnableMenu: =true
FillPortions: =1
Height: =Parent.Height
Items: =nfRbTableItems
LayoutMinHeight: =0
LayoutMinWidth: =0
OnMenuItemSelect: |-
=Switch(
cmpMyTableRb.SelectedMenuItem.ItemKey,
"view",
Set(
gblRbSelKey,
cmpMyTableRb.SelectedItem.CaseID
);
UpdateContext(
{
locRbPanelOpen: App.Width >= 1024,
locRbSheetOpen: App.Width < 1024
}
),
"createpo",
Notify(
"Create PO for " & cmpMyTableRb.SelectedItem.Title,
NotificationType.Success
),
"transfer",
Notify(
"Transfer " & cmpMyTableRb.SelectedItem.Title,
NotificationType.Information
),
"delete",
Set(
gblRbDeleteKey,
cmpMyTableRb.SelectedItem.CaseID
)
)
OnRowSelect: |-
=Set(gblRbSelKey, cmpMyTableRb.SelectedItem.CaseID);
UpdateContext({locRbPanelOpen: App.Width >= 1024, locRbSheetOpen: App.Width < 1024})
OnViewChange: =Set(gblRbView, cmpMyTableRb.SelectedViewMode)
PriorityConfig: |-
=Table(
{PriorityKey: "electronics", Background: "219,234,254,1", TextColor: "30,64,175,1"},
{PriorityKey: "hardware", Background: "237,233,254,1", TextColor: "91,33,182,1"},
{PriorityKey: "safety", Background: "255,237,213,1", TextColor: "154,52,18,1"},
{PriorityKey: "packaging", Background: "254,249,195,1", TextColor: "133,77,14,1"},
{PriorityKey: "cleaning", Background: "209,250,229,1", TextColor: "6,95,70,1"},
{PriorityKey: "office", Background: "224,231,255,1", TextColor: "55,48,163,1"},
{PriorityKey: "spare parts", Background: "241,245,249,1", TextColor: "51,65,85,1"},
{PriorityKey: "default", Background: "243,244,246,1", TextColor: "75,85,99,1"}
)
ProgressActiveColor: ="2563EB"
ProgressInactiveColor: ="E4E8F0"
ShowHeader: =true
ShowViewToggle: =false
StatusConfig: |-
=Table(
{StatusKey: "critical", Background: "254,242,242,1", TextColor: "127,29,29,1"},
{StatusKey: "low stock", Background: "255,251,235,1", TextColor: "120,53,15,1"},
{StatusKey: "active", Background: "240,253,244,1", TextColor: "20,83,45,1"},
{StatusKey: "inactive", Background: "248,250,252,1", TextColor: "51,65,85,1"},
{StatusKey: "default", Background: "243,244,246,1", TextColor: "75,85,99,1"}
)
ThemeConfig: |-
={
container: {background: "255,255,255,1", border: "228,232,240,1", borderThickness: 1, radius: 12},
header: {background: "248,250,252,1", border: "228,232,240,1", borderThickness: 2, textColor: "107,114,128,1", fontSize: 11, fontWeight: "Semibold", height: 44},
row: {background: "255,255,255,1", hoverBackground: "248,250,252,1", borderColor: "238,241,247,1", borderThickness: 1, height: 62},
card: {background: "255,255,255,1", border: "228,232,240,1", borderThickness: 1, radius: 12, height: 200, gap: 10},
list: {background: "255,255,255,1", border: "228,232,240,1", divider: "238,241,247,1", text: "17,24,39,1", textMuted: "156,163,175,1", radius: 10},
text: {primary: "17,24,39,1", secondary: "75,85,99,1", accent: "37,99,235,1", fontFamily: "Segoe UI", fontSizeNormal: 12, fontSizeSmall: 11, fontWeightNormal: "Normal", fontWeightBold: "Semibold"},
spacing: {paddingLeft: 16, paddingTop: 12, columnGap: 10}
}
ViewMode: =If(App.Width < 640, "list", gblRbView)
Visible: =nfRbCount > 0
Width: =Parent.Width
- conRbEmptyList:
Control: GroupContainer@1.5.0
Variant: AutoLayout
Properties:
DropShadow: =DropShadow.None
LayoutAlignItems: =LayoutAlignItems.Center
LayoutDirection: =LayoutDirection.Vertical
LayoutGap: =6
LayoutJustifyContent: =LayoutJustifyContent.Center
LayoutMinHeight: =0
Visible: =nfRbCount = 0
Children:
- lblRbEmptyHead:
Control: ModernText@1.0.0
Properties:
AutoHeight: =true
Color: =RGBA(17, 24, 39, 1)
FontWeight: =FontWeight.Semibold
LayoutMinHeight: =0
LayoutMinWidth: =0
Size: =14
Text: ="No items found"
- lblRbEmptyBody:
Control: ModernText@1.0.0
Properties:
Align: =Align.Center
AutoHeight: =true
Color: =RGBA(156, 163, 175, 1)
LayoutMinHeight: =0
LayoutMinWidth: =0
Size: =12
Text: ="Try adjusting your search or category filter"
- shpRbPanelDivider:
Control: Rectangle@2.3.0
Properties:
AlignInContainer: =AlignInContainer.Stretch
BorderStyle: =BorderStyle.None
Fill: =RGBA(228, 232, 240, 1)
LayoutMinWidth: =1
Visible: =locRbPanelOpen && App.Width >= 1024
Width: =1
- conRbPanel:
Control: GroupContainer@1.5.0
Variant: AutoLayout
Properties:
DropShadow: =DropShadow.None
Fill: =RGBA(255, 255, 255, 1)
FillPortions: =0
LayoutDirection: =LayoutDirection.Vertical
LayoutMinHeight: =0
LayoutMinWidth: =0
PaddingBottom: =12
PaddingTop: =12
Visible: =locRbPanelOpen && App.Width >= 1024
Width: =If(App.Width < 1200, 300, 348)
Children:
- conRbPanelHead:
Control: GroupContainer@1.5.0
Variant: AutoLayout
Properties:
DropShadow: =DropShadow.None
FillPortions: =0
Height: =50
LayoutAlignItems: =LayoutAlignItems.Center
LayoutDirection: =LayoutDirection.Horizontal
LayoutGap: =4
LayoutMinHeight: =0
PaddingLeft: =14
PaddingRight: =14
Children:
- btnRbPanelClose:
Control: Button@0.0.45
Properties:
Appearance: ='ButtonCanvas.Appearance'.Subtle
Height: =28
Icon: ="Dismiss"
LayoutMinHeight: =0
LayoutMinWidth: =0
OnSelect: |-
=Set(gblRbSelKey, "");
UpdateContext({locRbPanelOpen: false})
Text: =""
Width: =30
- btnRbPrev:
Control: Button@0.0.45
Properties:
Appearance: ='ButtonCanvas.Appearance'.Subtle
DisplayMode: =If(nfRbSelIndex <= 1, DisplayMode.Disabled, DisplayMode.Edit)
Height: =28
Icon: ="ChevronUp"
LayoutMinHeight: =0
LayoutMinWidth: =0
OnSelect: |-
=If(
nfRbSelIndex > 1,
Set(gblRbSelKey, Index(nfRbFiltered, nfRbSelIndex - 1).CaseID)
)
Text: =""
Width: =30
- btnRbNext:
Control: Button@0.0.45
Properties:
Appearance: ='ButtonCanvas.Appearance'.Subtle
DisplayMode: =If(nfRbSelIndex >= nfRbCount || nfRbSelIndex = 0, DisplayMode.Disabled, DisplayMode.Edit)
Height: =28
Icon: ="ChevronDown"
LayoutMinHeight: =0
LayoutMinWidth: =0
OnSelect: |-
=If(
nfRbSelIndex > 0 && nfRbSelIndex < nfRbCount,
Set(gblRbSelKey, Index(nfRbFiltered, nfRbSelIndex + 1).CaseID)
)
Text: =""
Width: =30
- lblRbPanelTitle:
Control: ModernText@1.0.0
Properties:
AutoHeight: =true
Color: =RGBA(17, 24, 39, 1)
FillPortions: =1
FontWeight: =FontWeight.Semibold
LayoutMinHeight: =0
LayoutMinWidth: =0
Size: =13
Text: =Coalesce(nfRbSelected.Title, "—")
- btnRbEdit:
Control: Button@0.0.45
Properties:
Appearance: ='ButtonCanvas.Appearance'.Secondary
Height: =28
Icon: ="Edit"
LayoutMinHeight: =0
LayoutMinWidth: =0
OnSelect: =Notify("Edit " & nfRbSelected.Title, NotificationType.Information)
Text: ="Edit"
Width: =74
- shpRbPanelHeadDivider:
Control: Rectangle@2.3.0
Properties:
AlignInContainer: =AlignInContainer.Stretch
BorderStyle: =BorderStyle.None
Fill: =RGBA(228, 232, 240, 1)
Height: =1
LayoutMinHeight: =1
- conRbPanelBody:
Control: GroupContainer@1.5.0
Variant: AutoLayout
Properties:
DropShadow: =DropShadow.None
LayoutDirection: =LayoutDirection.Vertical
LayoutGap: =14
LayoutMinHeight: =0
LayoutOverflowY: =LayoutOverflow.Scroll
PaddingBottom: =16
PaddingLeft: =16
PaddingRight: =16
PaddingTop: =16
Visible: =!IsBlank(gblRbSelKey)
Children:
- conRbHero:
Control: GroupContainer@1.5.0
Variant: AutoLayout
Properties:
DropShadow: =DropShadow.None
FillPortions: =0
Height: =52
LayoutAlignItems: =LayoutAlignItems.Center
LayoutDirection: =LayoutDirection.Horizontal
LayoutGap: =12
LayoutMinHeight: =0
Children:
- conRbThumb:
Control: GroupContainer@1.5.0
Variant: ManualLayout
Properties:
AlignInContainer: =AlignInContainer.Center
DropShadow: =DropShadow.None
Fill: =ColorFade(ColorValue(nfRbStatusHex), 0.88)
FillPortions: =0
Height: =44
LayoutMinWidth: =44
RadiusBottomLeft: =10
RadiusBottomRight: =10
RadiusTopLeft: =10
RadiusTopRight: =10
Width: =44
Children:
- imgRbThumbIcon:
Control: Image@2.2.3
Properties:
Height: =22
Image: =nfRbThumbIcon
Width: =22
X: =11
Y: =11
- conRbHeroInfo:
Control: GroupContainer@1.5.0
Variant: AutoLayout
Properties:
DropShadow: =DropShadow.None
LayoutDirection: =LayoutDirection.Vertical
LayoutGap: =1
LayoutJustifyContent: =LayoutJustifyContent.Center
LayoutMinHeight: =0
LayoutMinWidth: =0
Children:
- lblRbHeroName:
Control: ModernText@1.0.0
Properties:
AlignInContainer: =AlignInContainer.Stretch
AutoHeight: =true
Color: =RGBA(17, 24, 39, 1)
FontWeight: =FontWeight.Semibold
LayoutMinHeight: =0
LayoutMinWidth: =0
Size: =14
Text: =nfRbSelected.Title
- lblRbHeroSku:
Control: ModernText@1.0.0
Properties:
AlignInContainer: =AlignInContainer.Stretch
AutoHeight: =true
Color: =RGBA(156, 163, 175, 1)
LayoutMinHeight: =0
LayoutMinWidth: =0
Size: =11
Text: =nfRbSelected.CaseID & " · " & nfRbSelected.Status
- htmRbStockCard:
Control: HtmlViewer@2.1.0
Properties:
AlignInContainer: =AlignInContainer.Stretch
AutoHeight: =true
Font: =Font.'Segoe UI'
Height: =92
HtmlText: =nfRbStockCardHtml
LayoutMinHeight: =0
LayoutMinWidth: =0
PaddingBottom: =0
PaddingLeft: =0
PaddingRight: =0
PaddingTop: =0
- lblRbDetailsHead:
Control: ModernText@1.0.0
Properties:
AlignInContainer: =AlignInContainer.Stretch
AutoHeight: =true
Color: =RGBA(156, 163, 175, 1)
FontWeight: =FontWeight.Semibold
LayoutMinHeight: =0
LayoutMinWidth: =0
Size: =10
Text: ="DETAILS"
- galRbDetails:
Control: Gallery@2.15.0
Variant: Vertical
Properties:
DelayItemLoading: =false
FillPortions: =0
Height: =170
Items: =nfRbDetailRows
LayoutMinHeight: =0
LoadingSpinner: =LoadingSpinner.None
ShowScrollbar: =false
TemplatePadding: =0
TemplateSize: =34
Width: =Parent.Width - 32
Children:
- lblRbFieldLabel:
Control: Label@2.5.1
Properties:
Color: =RGBA(156, 163, 175, 1)
Font: =Font.'Segoe UI'
Height: =34
PaddingLeft: =0
Size: =12
Text: =ThisItem.FieldLabel
Width: =Parent.TemplateWidth / 2
- lblRbFieldValue:
Control: Label@2.5.1
Properties:
Align: =Align.Right
Color: =RGBA(17, 24, 39, 1)
Font: =Font.'Segoe UI'
Height: =34
PaddingRight: =0
Size: =12
Text: =ThisItem.FieldValue
Width: =Parent.TemplateWidth / 2
X: =Parent.TemplateWidth / 2
- shpRbFieldDivider:
Control: Rectangle@2.3.0
Properties:
BorderStyle: =BorderStyle.None
Fill: =RGBA(238, 241, 247, 1)
Height: =1
Width: =Parent.TemplateWidth
Y: =33
- lblRbActivityHead:
Control: ModernText@1.0.0
Properties:
AlignInContainer: =AlignInContainer.Stretch
AutoHeight: =true
Color: =RGBA(156, 163, 175, 1)
FontWeight: =FontWeight.Semibold
LayoutMinHeight: =0
LayoutMinWidth: =0
Size: =10
Text: ="RECENT ACTIVITY"
- cmpActivityTimelineRb:
Control: CanvasComponent
ComponentName: cmpActivityTimeline
Properties:
AlignInContainer: =AlignInContainer.Start
CardHeight: =104
FilterOptions: |-
=Table({Value: "All Activities"})
Height: =Parent.Height - 70
HideHeader: =true
IconMap: =nfRbTimelineIcons
IsLoading: =false
Items: =nfRbHistory
LayoutMinHeight: =450
LayoutMinWidth: =0
OnExport: =false
OnItemSelect: =false
OnPrint: =false
ShowScrollbar: =false
SubDetailCardHeight: =224
Theme: ="Light"
ThemeConfig: =nfRbTimelineTheme
Visible: =!IsEmpty(nfRbHistory)
Width: =Parent.Width - 24
- lblRbNoHistory:
Control: ModernText@1.0.0
Properties:
AlignInContainer: =AlignInContainer.Stretch
AutoHeight: =true
Color: =RGBA(156, 163, 175, 1)
LayoutMinHeight: =0
LayoutMinWidth: =0
Size: =12
Text: ="No movement recorded for this item"
Visible: =IsEmpty(nfRbHistory)
- conRbPanelEmpty:
Control: GroupContainer@1.5.0
Variant: AutoLayout
Properties:
DropShadow: =DropShadow.None
LayoutAlignItems: =LayoutAlignItems.Center
LayoutDirection: =LayoutDirection.Vertical
LayoutGap: =8
LayoutJustifyContent: =LayoutJustifyContent.Center
LayoutMinHeight: =0
PaddingLeft: =28
PaddingRight: =28
Visible: =IsBlank(gblRbSelKey)
Children:
- lblRbPanelEmptyHead:
Control: ModernText@1.0.0
Properties:
AutoHeight: =true
Color: =RGBA(17, 24, 39, 1)
FontWeight: =FontWeight.Semibold
LayoutMinHeight: =0
LayoutMinWidth: =0
Size: =14
Text: ="Select a record"
- lblRbPanelEmptyBody:
Control: ModernText@1.0.0
Properties:
Align: =Align.Center
AlignInContainer: =AlignInContainer.Stretch
AutoHeight: =true
Color: =RGBA(156, 163, 175, 1)
LayoutMinHeight: =0
LayoutMinWidth: =0
Size: =12
Text: ="Tap any row to view its details here, then step through results with the arrows above."
- cntBottomBarRb:
Control: GroupContainer@1.5.0
Variant: AutoLayout
Properties:
DropShadow: =DropShadow.None
Fill: =RGBA(255, 255, 255, 1)
FillPortions: =0
Height: =If(App.Width < 640, 58, 66)
LayoutAlignItems: =LayoutAlignItems.Center
LayoutDirection: =LayoutDirection.Vertical
LayoutMaxHeight: =If(App.Width < 640, 58, 66)
LayoutMinHeight: =If(App.Width < 640, 58, 66)
PaddingBottom: =4
PaddingTop: =4
Visible: =App.Width < 1024
Children:
- cmpBottomNavigationRb:
Control: CanvasComponent
ComponentName: cmpBottomNavigation
Properties:
ActiveColor: =RGBA(37, 99, 235, 1)
ActiveColorHex: ="2563EB"
ActiveIndicator: ="underline"
BadgeColor: =RGBA(220, 38, 38, 1)
Fill: =RGBA(255, 255, 255, 1)
Height: =Parent.Height - 8
InactiveColorHex: ="9CA3AF"
Items: |-
=Table(
{Index: 1, Icon: "dashboard", Label: "Dashboard", Badge: 0},
{Index: 2, Icon: "analytics", Label: "Items", Badge: 0},
{Index: 3, Icon: "bell", Label: "Alerts", Badge: 23},
{Index: 4, Icon: "user", Label: "Account", Badge: 0}
)
OnSelect: |-
=Set(gblRbActiveTab, cmpBottomNavigationRb.SelectedItemIndex);
Switch(
cmpBottomNavigationRb.SelectedItemIndex,
1, Notify("Navigate to Dashboard", NotificationType.Information),
2, false,
3, Notify("Navigate to Low Stock alerts", NotificationType.Information),
4, Notify("Navigate to Account", NotificationType.Information)
)
SelectedIndex: =gblRbActiveTab
ShowLabels: ="always"
Theme: ="Light"
Width: =Parent.Width
- cntLeftNavHostRb:
Control: GroupContainer@1.5.0
Variant: ManualLayout
Properties:
DropShadow: =DropShadow.None
Fill: =RGBA(13, 17, 23, 1)
Height: =Parent.Height
Visible: =App.Width >= 1024
Width: =If(_cmpNavIsExpanded, 224, 52)
Children:
- cmpSidebarRb:
Control: CanvasComponent
ComponentName: cmpSidebar
Properties:
AccentColor: =RGBA(37, 99, 235, 1)
AppLogo: =Blank()
AppName: ="Inventory"
AppNameAccent: ="Manager"
Expanded: =true
Height: =App.Height
Icons: |-
=Table(
{Name: "Dashboard", SVG: "<svg viewBox='0 0 24 24' width='24' height='24' xmlns='http://www.w3.org/2000/svg' fill='none' stroke='white' stroke-width='2' stroke-linecap='round' stroke-linejoin='round'><rect x='3' y='3' width='7' height='7' rx='1'/><rect x='14' y='3' width='7' height='7' rx='1'/><rect x='3' y='14' width='7' height='7' rx='1'/><rect x='14' y='14' width='7' height='7' rx='1'/></svg>"},
{Name: "List", SVG: "<svg viewBox='0 0 24 24' width='24' height='24' xmlns='http://www.w3.org/2000/svg' fill='none' stroke='white' stroke-width='2' stroke-linecap='round' stroke-linejoin='round'><line x1='3' y1='6' x2='21' y2='6'/><line x1='3' y1='12' x2='21' y2='12'/><line x1='3' y1='18' x2='21' y2='18'/></svg>"},
{Name: "Cart", SVG: "<svg viewBox='0 0 24 24' width='24' height='24' xmlns='http://www.w3.org/2000/svg' fill='none' stroke='white' stroke-width='2' stroke-linecap='round' stroke-linejoin='round'><rect x='3' y='5' width='18' height='16' rx='2'/><line x1='8' y1='5' x2='8' y2='3'/><line x1='16' y1='5' x2='16' y2='3'/><line x1='3' y1='10' x2='21' y2='10'/></svg>"},
{Name: "Transfer", SVG: "<svg viewBox='0 0 24 24' width='24' height='24' xmlns='http://www.w3.org/2000/svg' fill='none' stroke='white' stroke-width='2' stroke-linecap='round' stroke-linejoin='round'><polyline points='16 3 21 8 16 13'/><line x1='21' y1='8' x2='7' y2='8'/><polyline points='8 21 3 16 8 11'/><line x1='3' y1='16' x2='17' y2='16'/></svg>"},
{Name: "Barcode", SVG: "<svg viewBox='0 0 24 24' width='24' height='24' xmlns='http://www.w3.org/2000/svg' fill='none' stroke='white' stroke-width='2' stroke-linecap='round' stroke-linejoin='round'><path d='M3 8V5a2 2 0 0 1 2-2h3'/><path d='M16 3h3a2 2 0 0 1 2 2v3'/><path d='M3 16v3a2 2 0 0 0 2 2h3'/><path d='M16 21h3a2 2 0 0 0 2-2v-3'/><line x1='9' y1='8' x2='9' y2='16'/><line x1='13' y1='8' x2='13' y2='16'/></svg>"},
{Name: "Bell", SVG: "<svg viewBox='0 0 24 24' width='24' height='24' xmlns='http://www.w3.org/2000/svg' fill='none' stroke='white' stroke-width='2' stroke-linecap='round' stroke-linejoin='round'><path d='M18 8A6 6 0 0 0 6 8c0 7-3 9-3 9h18s-3-2-3-9'/><path d='M13.73 21a2 2 0 0 1-3.46 0'/></svg>"},
{Name: "Export", SVG: "<svg viewBox='0 0 24 24' width='24' height='24' xmlns='http://www.w3.org/2000/svg' fill='none' stroke='white' stroke-width='2' stroke-linecap='round' stroke-linejoin='round'><path d='M21 15v4a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2v-4'/><polyline points='7 10 12 15 17 10'/><line x1='12' y1='15' x2='12' y2='3'/></svg>"}
)
Items: |-
=Table(
{ID: 100, Title: "OVERVIEW", Icon: "", Letter: "", Badge: 0, BadgeColor: RGBA(0, 0, 0, 0), BadgeDot: false, IsSection: true, ParentID: -1, Visible: true},
{ID: 1, Title: "Dashboard", Icon: "Dashboard", Letter: "", Badge: 0, BadgeColor: RGBA(0, 0, 0, 0), BadgeDot: false, IsSection: false, ParentID: -1, Visible: true},
{ID: 2, Title: "All Items", Icon: "List", Letter: "", Badge: 0, BadgeColor: RGBA(0, 0, 0, 0), BadgeDot: false, IsSection: false, ParentID: -1, Visible: true},
{ID: 101, Title: "OPERATIONS", Icon: "", Letter: "", Badge: 0, BadgeColor: RGBA(0, 0, 0, 0), BadgeDot: false, IsSection: true, ParentID: -1, Visible: true},
{ID: 3, Title: "Purchase Orders", Icon: "Cart", Letter: "", Badge: 0, BadgeColor: RGBA(0, 0, 0, 0), BadgeDot: false, IsSection: false, ParentID: -1, Visible: true},
{ID: 4, Title: "Transfers", Icon: "Transfer", Letter: "", Badge: 0, BadgeColor: RGBA(0, 0, 0, 0), BadgeDot: false, IsSection: false, ParentID: -1, Visible: true},
{ID: 5, Title: "Barcode Scan", Icon: "Barcode", Letter: "", Badge: 0, BadgeColor: RGBA(0, 0, 0, 0), BadgeDot: false, IsSection: false, ParentID: -1, Visible: true},
{ID: 102, Title: "ALERTS", Icon: "", Letter: "", Badge: 0, BadgeColor: RGBA(0, 0, 0, 0), BadgeDot: false, IsSection: true, ParentID: -1, Visible: true},
{ID: 6, Title: "Low Stock", Icon: "Bell", Letter: "", Badge: 23, BadgeColor: RGBA(220, 38, 38, 1), BadgeDot: false, IsSection: false, ParentID: -1, Visible: true},
{ID: 103, Title: "REPORTS", Icon: "", Letter: "", Badge: 0, BadgeColor: RGBA(0, 0, 0, 0), BadgeDot: false, IsSection: true, ParentID: -1, Visible: true},
{ID: 7, Title: "Export", Icon: "Export", Letter: "", Badge: 0, BadgeColor: RGBA(0, 0, 0, 0), BadgeDot: false, IsSection: false, ParentID: -1, Visible: true}
)
OnFooterSettings: =Notify("Settings coming soon", NotificationType.Information)
OnHelp: =Notify("Help coming soon", NotificationType.Information)
OnNavSelect: |-
=Switch(
cmpSidebarRb.SelectedID,
1, Notify("Navigate to Dashboard", NotificationType.Information),
2, false,
3, Notify("Purchase Orders coming soon", NotificationType.Information),
4, Notify("Transfers coming soon", NotificationType.Information),
5, Notify("Barcode Scan coming soon", NotificationType.Information),
6, Notify("Low Stock alerts coming soon", NotificationType.Information),
7, Notify("Export coming soon", NotificationType.Information)
)
OnThemeToggle: =false
OnToggle: =false
ShowFooterMenu: =true
ShowHeader: =true
ShowSearch: =false
ShowToggle: =true
ShowUser: =true
Theme: ="Dark"
Width: =If(_cmpNavIsExpanded, 224, 52)
- cmpFabRb:
Control: CanvasComponent
ComponentName: cmpFloatingActionButton
Properties:
Color: =RGBA(37, 99, 235, 1)
Disabled: =false
Fill: =Color.Transparent
Height: |-
=With(
{_fabSize: Switch(cmpFabRb.Size, "small", 40, "regular", 56, "large", 72, 56)},
If(
_fabOpen && CountRows(cmpFabRb.SpeedDialItems) > 0,
_fabSize + 12 + CountRows(cmpFabRb.SpeedDialItems) * cmpFabRb.ItemSize,
_fabSize
)
)
Icon: ="add"
IconColor: ="#FFFFFF"
ItemSize: =52
Label: ="Add Item"
OnSelect: =false
OnSpeedDialSelect: |-
=Switch(
Self.SelectedSpeedDialItem.Label,
"Add Item", Notify("Add item coming soon", NotificationType.Information),
"Scan Barcode", Notify("Barcode scan coming soon", NotificationType.Information),
"Create PO", Notify("Create PO coming soon", NotificationType.Information)
)
Size: ="regular"
SpeedDialItems: |-
=Table(
{Icon: "Plus", Label: "Add Item", Color: RGBA(37, 99, 235, 1)},
{Icon: "Plus", Label: "Scan Barcode", Color: RGBA(8, 145, 178, 1)},
{Icon: "Plus", Label: "Create PO", Color: RGBA(124, 58, 237, 1)}
)
Style: ="standard"
Theme: ="Light"
Visible: =App.Width < 640
Width: |-
=With(
{
_fabSize: Switch(cmpFabRb.Size, "small", 40, "regular", 56, "large", 72, 56),
_maxLabelChars: If(
CountRows(cmpFabRb.SpeedDialItems) > 0,
Max(cmpFabRb.SpeedDialItems, Len(Label)),
0
),
_iconSize: Switch(cmpFabRb.Size, "small", 20, "regular", 24, "large", 36, 24)
},
If(
_fabOpen && CountRows(cmpFabRb.SpeedDialItems) > 0,
Min(Min(_maxLabelChars * 8 + 64, 240) + Switch(cmpFabRb.Size, "small", 20, "regular", 28, "large", 36, 28) + 20, App.Width),
If(
cmpFabRb.Style = "extended",
16 + _iconSize + 8 + Min(Len(cmpFabRb.Label) * 8, 220) + 20,
_fabSize
)
)
)
X: =Parent.Width - Self.Width - 20
Y: =Parent.Height - Self.Height - 78
- htmRbSheetScrim:
Control: HtmlViewer@2.1.0
Properties:
Font: =Font.'Segoe UI'
Height: =Parent.Height
HtmlText: ="<div style='display:block;width:" & Self.Width & "px;height:" & Self.Height - 1 & "px;background:rgba(15,23,42,0.42);backdrop-filter:blur(3px)'></div>"
OnSelect: |-
=Set(gblRbSelKey, "");
UpdateContext({locRbSheetOpen: false})
PaddingBottom: =0
PaddingLeft: =0
PaddingRight: =0
PaddingTop: =0
Visible: =locRbSheetOpen && App.Width < 1024
Width: =Parent.Width
- conRbSheet:
Control: GroupContainer@1.5.0
Variant: AutoLayout
Properties:
DropShadow: =DropShadow.Bold
Fill: =RGBA(255, 255, 255, 1)
Height: =Min(Parent.Height * 0.86, 620)
LayoutDirection: =LayoutDirection.Vertical
RadiusTopLeft: =18
RadiusTopRight: =18
Visible: =locRbSheetOpen && App.Width < 1024
Width: =Parent.Width
Y: =Parent.Height - Self.Height
Children:
- conRbSheetHandleRow:
Control: GroupContainer@1.5.0
Variant: AutoLayout
Properties:
DropShadow: =DropShadow.None
FillPortions: =0
Height: =18
LayoutAlignItems: =LayoutAlignItems.Center
LayoutDirection: =LayoutDirection.Vertical
LayoutJustifyContent: =LayoutJustifyContent.Center
LayoutMinHeight: =0
Children:
- shpRbSheetHandle:
Control: Rectangle@2.3.0
Properties:
BorderStyle: =BorderStyle.None
Fill: =RGBA(209, 213, 219, 1)
Height: =4
LayoutMinHeight: =4
LayoutMinWidth: =38
Width: =38
- conRbSheetHead:
Control: GroupContainer@1.5.0
Variant: AutoLayout
Properties:
DropShadow: =DropShadow.None
FillPortions: =0
Height: =44
LayoutAlignItems: =LayoutAlignItems.Center
LayoutDirection: =LayoutDirection.Horizontal
LayoutGap: =8
LayoutMinHeight: =0
PaddingLeft: =16
PaddingRight: =12
Children:
- lblRbSheetTitle:
Control: ModernText@1.0.0
Properties:
AutoHeight: =true
Color: =RGBA(17, 24, 39, 1)
FillPortions: =1
FontWeight: =FontWeight.Semibold
LayoutMinHeight: =0
LayoutMinWidth: =0
Size: =14
Text: =Coalesce(nfRbSelected.Title, "Detail")
- btnRbSheetEdit:
Control: Button@0.0.45
Properties:
Appearance: ='ButtonCanvas.Appearance'.Secondary
Height: =30
Icon: ="Edit"
LayoutMinHeight: =0
LayoutMinWidth: =0
OnSelect: =Notify("Edit " & nfRbSelected.Title, NotificationType.Information)
Text: =""
Width: =36
- btnRbSheetPrev:
Control: Button@0.0.45
Properties:
Appearance: ='ButtonCanvas.Appearance'.Subtle
DisplayMode: =If(nfRbSelIndex <= 1, DisplayMode.Disabled, DisplayMode.Edit)
Height: =30
Icon: ="ChevronUp"
LayoutMinHeight: =0
LayoutMinWidth: =0
OnSelect: |-
=If(
nfRbSelIndex > 1,
Set(gblRbSelKey, Index(nfRbFiltered, nfRbSelIndex - 1).CaseID)
)
Text: =""
Width: =32
- btnRbSheetNext:
Control: Button@0.0.45
Properties:
Appearance: ='ButtonCanvas.Appearance'.Subtle
DisplayMode: =If(nfRbSelIndex >= nfRbCount || nfRbSelIndex = 0, DisplayMode.Disabled, DisplayMode.Edit)
Height: =30
Icon: ="ChevronDown"
LayoutMinHeight: =0
LayoutMinWidth: =0
OnSelect: |-
=If(
nfRbSelIndex > 0 && nfRbSelIndex < nfRbCount,
Set(gblRbSelKey, Index(nfRbFiltered, nfRbSelIndex + 1).CaseID)
)
Text: =""
Width: =32
- btnRbSheetClose:
Control: Button@0.0.45
Properties:
Appearance: ='ButtonCanvas.Appearance'.Subtle
Height: =30
Icon: ="Dismiss"
LayoutMinHeight: =0
LayoutMinWidth: =0
OnSelect: |-
=Set(gblRbSelKey, "");
UpdateContext({locRbSheetOpen: false})
Text: =""
Width: =32
- shpRbSheetDivider:
Control: Rectangle@2.3.0
Properties:
AlignInContainer: =AlignInContainer.Stretch
BorderStyle: =BorderStyle.None
Fill: =RGBA(228, 232, 240, 1)
Height: =1
LayoutMinHeight: =1
- conRbSheetBody:
Control: GroupContainer@1.5.0
Variant: AutoLayout
Properties:
DropShadow: =DropShadow.None
LayoutDirection: =LayoutDirection.Vertical
LayoutGap: =14
LayoutMinHeight: =0
LayoutOverflowY: =LayoutOverflow.Scroll
PaddingBottom: =20
PaddingLeft: =16
PaddingRight: =16
PaddingTop: =14
Children:
- conRbSheetHero:
Control: GroupContainer@1.5.0
Variant: AutoLayout
Properties:
DropShadow: =DropShadow.None
FillPortions: =0
Height: =52
LayoutAlignItems: =LayoutAlignItems.Center
LayoutDirection: =LayoutDirection.Horizontal
LayoutGap: =12
LayoutMinHeight: =0
Children:
- conRbSheetThumb:
Control: GroupContainer@1.5.0
Variant: ManualLayout
Properties:
AlignInContainer: =AlignInContainer.Center
DropShadow: =DropShadow.None
Fill: =ColorFade(ColorValue(nfRbStatusHex), 0.88)
FillPortions: =0
Height: =44
LayoutMinWidth: =44
RadiusBottomLeft: =10
RadiusBottomRight: =10
RadiusTopLeft: =10
RadiusTopRight: =10
Width: =44
Children:
- imgRbSheetThumbIcon:
Control: Image@2.2.3
Properties:
Height: =22
Image: =nfRbThumbIcon
Width: =22
X: =11
Y: =11
- conRbSheetHeroInfo:
Control: GroupContainer@1.5.0
Variant: AutoLayout
Properties:
DropShadow: =DropShadow.None
LayoutDirection: =LayoutDirection.Vertical
LayoutGap: =1
LayoutJustifyContent: =LayoutJustifyContent.Center
LayoutMinHeight: =0
LayoutMinWidth: =0
Children:
- lblRbSheetHeroName:
Control: ModernText@1.0.0
Properties:
AlignInContainer: =AlignInContainer.Stretch
AutoHeight: =true
Color: =RGBA(17, 24, 39, 1)
FontWeight: =FontWeight.Semibold
LayoutMinHeight: =0
LayoutMinWidth: =0
Size: =14
Text: =nfRbSelected.Title
- lblRbSheetHeroSku:
Control: ModernText@1.0.0
Properties:
AlignInContainer: =AlignInContainer.Stretch
AutoHeight: =true
Color: =RGBA(156, 163, 175, 1)
LayoutMinHeight: =0
LayoutMinWidth: =0
Size: =11
Text: =nfRbSelected.CaseID & " · " & nfRbSelected.Status
- htmRbSheetStockCard:
Control: HtmlViewer@2.1.0
Properties:
AlignInContainer: =AlignInContainer.Stretch
AutoHeight: =true
Font: =Font.'Segoe UI'
Height: =92
HtmlText: =nfRbStockCardHtml
LayoutMinHeight: =0
LayoutMinWidth: =0
PaddingBottom: =0
PaddingLeft: =0
PaddingRight: =0
PaddingTop: =0
- lblRbSheetDetailsHead:
Control: ModernText@1.0.0
Properties:
AlignInContainer: =AlignInContainer.Stretch
AutoHeight: =true
Color: =RGBA(156, 163, 175, 1)
FontWeight: =FontWeight.Semibold
LayoutMinHeight: =0
LayoutMinWidth: =0
Size: =10
Text: ="DETAILS"
- galRbSheetDetails:
Control: Gallery@2.15.0
Variant: Vertical
Properties:
DelayItemLoading: =false
FillPortions: =0
Height: =170
Items: =nfRbDetailRows
LayoutMinHeight: =0
LoadingSpinner: =LoadingSpinner.None
ShowScrollbar: =false
TemplatePadding: =0
TemplateSize: =34
Width: =Parent.Width - 32
Children:
- lblRbSheetFieldLabel:
Control: Label@2.5.1
Properties:
Color: =RGBA(156, 163, 175, 1)
Font: =Font.'Segoe UI'
Height: =34
PaddingLeft: =0
Size: =12
Text: =ThisItem.FieldLabel
Width: =Parent.TemplateWidth / 2
- lblRbSheetFieldValue:
Control: Label@2.5.1
Properties:
Align: =Align.Right
Color: =RGBA(17, 24, 39, 1)
Font: =Font.'Segoe UI'
Height: =34
PaddingRight: =0
Size: =12
Text: =ThisItem.FieldValue
Width: =Parent.TemplateWidth / 2
X: =Parent.TemplateWidth / 2
- shpRbSheetFieldDivider:
Control: Rectangle@2.3.0
Properties:
BorderStyle: =BorderStyle.None
Fill: =RGBA(238, 241, 247, 1)
Height: =1
Width: =Parent.TemplateWidth
Y: =33
- lblRbSheetActivityHead:
Control: ModernText@1.0.0
Properties:
AlignInContainer: =AlignInContainer.Stretch
AutoHeight: =true
Color: =RGBA(156, 163, 175, 1)
FontWeight: =FontWeight.Semibold
LayoutMinHeight: =0
LayoutMinWidth: =0
Size: =10
Text: ="RECENT ACTIVITY"
- cmpActivityTimelineSheetRb:
Control: CanvasComponent
ComponentName: cmpActivityTimeline
Properties:
AlignInContainer: =AlignInContainer.Start
CardHeight: =104
FilterOptions: |-
=Table({Value: "All Activities"})
Height: =Parent.Height - 70
HideHeader: =true
IconMap: =nfRbTimelineIcons
IsLoading: =false
Items: =nfRbHistory
LayoutMinHeight: =380
LayoutMinWidth: =0
OnExport: =false
OnItemSelect: =false
OnPrint: =false
ShowScrollbar: =false
SubDetailCardHeight: =224
Theme: ="Light"
ThemeConfig: =nfRbTimelineTheme
Visible: =!IsEmpty(nfRbHistory)
Width: =Parent.Width - 24
- lblRbSheetNoHistory:
Control: ModernText@1.0.0
Properties:
AlignInContainer: =AlignInContainer.Stretch
AutoHeight: =true
Color: =RGBA(156, 163, 175, 1)
LayoutMinHeight: =0
LayoutMinWidth: =0
Size: =12
Text: ="No movement recorded for this item"
Visible: =IsEmpty(nfRbHistory)
- cmpRbDeleteDialog:
Control: CanvasComponent
ComponentName: cmpEnhancedDialog
Properties:
BorderRadius: =12
Buttons: |-
=Table(
{Label: "Cancel", ButtonType: "Secondary", Action: "Cancel", Visible: true},
{Label: "Delete", ButtonType: "Primary", Action: "Delete", Visible: true}
)
DialogHeight: =0
DialogType: ="Confirmation"
DialogWidth: =460
HeaderText: ="Delete item?"
Height: =App.Height
IconName: ="Error"
IsTextInputRequired: =false
MessageText: ="Are you sure you want to delete """ & cmpMyTableRb.SelectedItem.Title & """?"
OnButtonSelect: |-
=If(
cmpRbDeleteDialog.SelectedButton.Action = "Delete",
// SWAP POINT: against a real source this is
// Remove(YourList, LookUp(YourList, CaseID = gblRbDeleteKey))
RemoveIf(colRbItems, CaseID = gblRbDeleteKey);
If(
gblRbSelKey = gblRbDeleteKey,
Set(gblRbSelKey, "");
UpdateContext({locRbPanelOpen: false, locRbSheetOpen: false})
);
Notify("Deleted " & gblRbDeleteKey, NotificationType.Success)
);
Set(gblRbDeleteKey, "")
OnCloseSelect: =Set(gblRbDeleteKey, "")
OverlayBlurColor: ="RGBA(15, 23, 42, 0.45)"
PlaceholderText: ="Reason"
ShowCloseIcon: =true
Theme: ="Light"
Visible: =!IsBlank(gblRbDeleteKey)
Width: =App.WidthHow It Works
1Filtering and sorting are declarative named formulas
nfRbFiltered is a single App.Formula that filters colRbItems by gblRbCategory + gblRbSearch, then sorts by gblRbSortCol / gblRbSortDir. It re-evaluates automatically whenever a global changes — no ClearCollect, no OnChange handlers.
nfRbFiltered =
Switch(gblRbSortCol,
"OnHand", Sort(_f, OnHand, _d),
Sort(_f, Title, _d))2Selection is keyed on CaseID, then looked back up
cmpMyTable.SelectedItem only returns the component's contract columns, so the row's full record isn't available on select. gblRbSelKey stores the CaseID and nfRbSelected = LookUp(colRbItems, CaseID = gblRbSelKey) rehydrates everything the detail view needs.
nfRbSelected = LookUp(colRbItems, CaseID = gblRbSelKey)3The Data Table's stock bar reuses CompletedSteps / TotalSteps
cmpMyTable's segmented progress bar caps at 5 segments, so nfRbTableItems scales raw OnHand / Reorder down to a 0–5 bucket and feeds it in as CompletedSteps. The real numbers show in the detail panel's stock card.
nfRbTableItems = AddColumns(nfRbFiltered,
CompletedSteps, RoundUp(Min(OnHand / Max(Reorder,1), 1) * 5, 0),
TotalSteps, 5)4The stock ring is a CSS conic-gradient, not an SVG
HtmlViewer strips inline <svg>, so nfRbStockCardHtml builds the progress ring with a conic-gradient. It's assembled once as a named formula and shared by both the desktop panel and the mobile sheet so they can never drift.
background:conic-gradient(
<status> 0% <pct>%, #E4E8F0 <pct>% 100%)5Timeline config is set explicitly on both instances
The Activity Timeline appears twice (panel + mobile sheet). A Table or Record input left out of pasted YAML arrives blank — which renders the timeline black — so nfRbTimelineIcons and nfRbTimelineTheme are wired to both instances explicitly.
cmpActivityTimeline.IconMap = nfRbTimelineIcons
cmpActivityTimeline.ThemeConfig = nfRbTimelineThemeCustomization
Check your column shapes first
Before mapping anything, print one record so you can see the real field names and how Choice and Lookup columns are structured. Drop a temporary label on the screen and set its Text to the formula below. Blank columns with no error almost always trace back to one of these two things.
JSON(
First('YourList'),
JSONFormat.IgnoreBinaryData & JSONFormat.IgnoreUnsupportedTypes & JSONFormat.IndentFour
)Choice and Lookup columns come back as records, so they need .Value. SharePoint internal names are fixed at creation, so a column created as "Issue Title" stays Issue_x0020_Title even if it displays differently now. Reference names with spaces in quotes: _i.'Issue Title'.
Connect your data
Replace the ClearCollect(colRbItems, Table(…)) seed in the screen's OnVisible with a ForAll that builds each record explicitly. ForAll is preferred over AddColumns here because the output shape is visible in one place and there is no column-name collision if your source already has a Title column, which SharePoint always does.
ClearCollect(colRbItems,
ForAll(
'YourList' As _i,
{
ItemID: _i.ID,
CaseID: _i.YourReferenceColumn,
Title: _i.YourNameColumn,
Status: _i.YourStatusChoice.Value,
Category: _i.YourCategoryLookup.Value,
Assignee: _i.YourOwnerColumn,
DueDate: _i.YourDateColumn,
StatusRank: Switch(_i.YourStatusChoice.Value,
"First", 1, "Second", 2, "Third", 3, 9),
CompletedSteps: 3,
TotalSteps: 5
}
)
);Extra columns ride along and are available in the detail panel through nfRbSelected, so add whatever your panel needs.
Required columns
Seven columns are load-bearing; everything else is optional. CaseID is the selection key, used by the panel, the prev and next stepper and the delete dialog, so it has to be unique. Title shows in the table, the cards and the panel header. Status must match a StatusConfig key in cmpMyTable, matched case-insensitively. Category must match a colRbCats label exactly, since the filter is a plain text comparison. Assignee fills the table column and the panel. CompletedSteps and TotalSteps drive the progress bar, which caps at 5 segments in table view. StatusRank gives status sorting a meaningful order instead of alphabetical.
Change the categories
Edit colRbCats (seeded in OnVisible) and the chip labels. The filter compares Category = gblRbCategory as plain text, so labels just need to match your data exactly. Hand-list them rather than deriving them from Distinct(), whose output column is named Value and is awkward to sort.
Adjust status colors
nfRbStatusHex maps Status to hex. It drives the badge, the stock ring and the category thumbnail in one place, so edit it once and everything follows.
Add a sort option
Add a row to colRbSortOptions where Col is a real column name on colRbItems, then add a matching branch to the Switch in nfRbFiltered. The dropdown and the direction toggle pick it up automatically.
Change the detail fields
nfRbDetailRows is a small Table of {FieldLabel, FieldValue} rows rendered in the panel. Add, remove, or reorder rows and the panel gallery follows automatically.
Point the timeline at real history
colRbHistory seeds the per-record activity, keyed on CaseID so each record shows only its own events. Swap it for your history list and the timeline and icons update through nfRbTimelineIcons. No history list? Build one from SharePoint's built-in audit columns, which every list has. Note this reads colRbItems rather than the list, so it costs no extra network call.
ClearCollect(colRbHistory,
ForAll(
colRbItems As _r,
{
CaseID: _r.CaseID,
EventTitle: "Created",
EventDescription: _r.Category,
Author: _r.CreatedBy,
DisplayDateTime: _r.CreatedOn,
Category: "Created",
HasSubDetail: false,
SubTo: "", SubCC: "", SubPriority: "", SubMessage: ""
}
)
);That needs CreatedBy: _i.'Created By'.DisplayName and CreatedOn: Text(_i.Created, "[$-en-US]mmm d, yyyy h:mm AM/PM") added to the main load.
If your data has no quantities
The stock card, the ring and the on hand / reorder / value rows assume inventory data. Either repoint the ring at any 0 to 100 percentage (a lifecycle stage works well), or delete htmRbStockCard and htmRbSheetStockCard from the screen along with nfRbStockCardHtml and nfRbStockPct from App.Formulas. If you delete it, put a label bound to your description field in that space or the panel looks thin.
nfRbStockPct = Min(100, RoundDown(nfRbSelected.CompletedSteps / 5 * 100, 0));Delegation and row limits
The template loads into a collection and filters in memory, so everything after load is instant and nothing after load is delegable. The only constraint is the load itself. Under 2000 rows this works as shipped; raise the Data row limit in App Settings from the default 500 to 2000. Above that, drop the ClearCollect and point nfRbFiltered at the source directly, keeping to delegable operators only: StartsWith on text, and equals on Choice and Lookup columns. Search(), in, and comparing two columns to each other are all non-delegable. That last one fails loudly rather than quietly, returning "Fetching items failed. Possible invalid string in filter query" instead of a delegation warning.
After pasting
Navigate to another screen and back so OnVisible re-fires. If you see errors that do not match the code you just pasted, old collections are still in memory from the previous run. Close and reopen the app, or clear them under View > Collections.
Get new components and templates when they drop
One email when something new ships. Unsubscribe anytime.
Community
Use the toolbar to format · or type markdown directly