Automates the production manager's manual workflow of checking xTuple WO Schedule + Kit Material Shortage for FA department work orders. Two tabs: WO Shortages (detail per WO + shortage line with customer name, YYYY-MM-DD dates) and Critical Parts (aggregated parts blocking near-term shipments with QOH from MPE warehouse). Nav button added to all pages. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
6.7 KiB
name, description
| name | description |
|---|---|
| add-tabs-to-page | Adds a TABS_WIDGET to a page so multiple data views live under one page instead of separate pages. Use when the user asks to add tabs, consolidate pages into tabs, create tabbed views, or replace a single table with a tabbed layout. |
Add Tabs to a Page
Replaces a page's standalone content (e.g. a single TABLE_WIDGET_V2) with a TABS_WIDGET containing multiple tabs, each with its own widgets and query binding.
Prerequisites
- Tab definitions: User must provide the tab labels and the query each tab should display.
- Page prefix: Each page uses a short widget-ID prefix (e.g.
pa1,cp1). Reuse the existing prefix from the page's widgets. - Follow the add-query-to-table skill for creating any new queries needed by the tabs.
File Layout
The Tabs widget and all widgets inside tabs live under a Tabs1/ directory:
pages/<PageName>/widgets/Tabs1/
├── Tabs1.json # The TABS_WIDGET itself
├── <Table1>.json # Table inside tab 1
├── <Table2>.json # Table inside tab 2
├── <DatePicker>.json # Optional: date pickers inside a tab
└── ...
If the page previously had a standalone Table1.json at widgets/Table1.json, delete it after creating the tabbed replacement.
Instructions
1. Design the tab structure
Decide on tab count, labels, and content. Example:
| Tab ID | Label | Canvas widgetId | Content |
|---|---|---|---|
| tab1 | All | {prefix}cnvsall |
TableAll bound to query_all |
| tab2 | SLx | {prefix}cnvsslx |
TableSLx bound to query_slx |
2. Create Tabs1.json
The TABS_WIDGET has three critical sections:
A) tabsObj — declares each tab with id, label, index, and canvas widgetId:
"tabsObj": {
"tab1": {
"id": "tab1",
"index": 0,
"isVisible": true,
"label": "All",
"positioning": "vertical",
"widgetId": "<canvas-widgetId-for-tab1>"
},
"tab2": { ... }
}
B) children — array of CANVAS_WIDGET entries, one per tab. Each canvas must reference:
"parentId": the Tabs widget'swidgetId"tabId": matching key fromtabsObj(e.g."tab1")"tabName": the display label"widgetId": unique canvas ID (referenced intabsObjand by child widgets)
**C) Top-level Tabs properties:
{
"type": "TABS_WIDGET",
"version": 3,
"isCanvas": true,
"shouldShowTabs": true,
"defaultTab": "<label of default tab>",
"parentId": "0",
"leftColumn": 9,
"rightColumn": 64,
"topRow": 5,
"bottomRow": 67,
"dynamicHeight": "AUTO_HEIGHT",
"dynamicBindingPathList": [
{"key": "accentColor"},
{"key": "boxShadow"}
],
"accentColor": "{{appsmith.theme.colors.primaryColor}}",
"boxShadow": "{{appsmith.theme.boxShadow.appBoxShadow}}",
"backgroundColor": "#FFFFFF",
"borderColor": "#E0DEDE",
"borderRadius": "0.375rem",
"borderWidth": 1
}
3. Create widgets inside each tab
Each widget inside a tab sets "parentId" to that tab's canvas widgetId (not the Tabs widgetId). Widgets start at topRow: 0 within each canvas.
Table-only tabs (no date pickers):
{
"type": "TABLE_WIDGET_V2",
"parentId": "<canvas-widgetId>",
"topRow": 0,
"bottomRow": 58,
"leftColumn": 0,
"rightColumn": 62,
"tableData": "{{<query_name>.data}}",
...
}
Tabs with date pickers (see Sales - Units Shipped for reference):
| Widget | topRow | bottomRow | leftColumn | rightColumn |
|---|---|---|---|---|
| DateFrom | 0 | 7 | 0 | 5 |
| DateTo | 0 | 7 | 5 | 10 |
| Table | 7 | 58 | 0 | 62 |
Date pickers use "type": "DATE_PICKER_WIDGET2", "dateFormat": "YYYY-MM-DD HH:mm", and "parentId" set to the tab's canvas widgetId.
4. Create queries for each tab
Follow the add-query-to-table skill. Each query's metadata.json must have:
"pageId": the page name (e.g."Pending POs")"gitSyncId": use the same 24-char hex prefix as the page, with a unique UUID suffix"runBehaviour": "AUTOMATIC"
5. Delete the old standalone widget
If the page had a widgets/Table1.json (or similar) that the Tabs widget replaces, delete it.
6. Verify
- Each canvas
widgetIdinchildrenmatches the correspondingwidgetIdintabsObj. - Each widget inside a tab has
parentIdset to its tab's canvaswidgetId. defaultTabmatches one of the tab labels (not the tab ID).- All queries reference the correct
pageId. - Run
git status/git diffto confirm only intended changes.
Canvas Widget Template
Each canvas child in the children array follows this template:
{
"borderRadius": "{{appsmith.theme.borderRadius.appBorderRadius}}",
"bottomRow": 620,
"boxShadow": "{{appsmith.theme.boxShadow.appBoxShadow}}",
"canExtend": true,
"detachFromLayout": true,
"dynamicBindingPathList": [
{"key": "borderRadius"},
{"key": "boxShadow"}
],
"dynamicHeight": "AUTO_HEIGHT",
"dynamicTriggerPathList": [],
"flexLayers": [],
"isDisabled": false,
"isLoading": false,
"isVisible": true,
"key": "<unique-key>",
"leftColumn": 0,
"maxDynamicHeight": 9000,
"minDynamicHeight": 4,
"minHeight": 150,
"minWidth": 450,
"mobileBottomRow": 150,
"mobileLeftColumn": 0,
"mobileRightColumn": 602.625,
"mobileTopRow": 0,
"needsErrorInfo": false,
"parentColumnSpace": 1,
"parentId": "<tabs-widgetId>",
"parentRowSpace": 1,
"renderMode": "CANVAS",
"responsiveBehavior": "fill",
"rightColumn": 602.625,
"shouldScrollContents": false,
"tabId": "<tab-id>",
"tabName": "<tab-label>",
"topRow": 0,
"type": "CANVAS_WIDGET",
"version": 1,
"widgetId": "<unique-canvas-widgetId>",
"widgetName": "Canvas<N>"
}
Consolidating Multiple Pages into Tabs
When merging separate pages into one tabbed page:
- Choose the surviving page (or create a new one via create-new-page skill).
- Move each page's query into the surviving page's
queries/directory. UpdatepageIdand generate newgitSyncIdvalues (same 24-char prefix as the surviving page). - Create the Tabs widget with one tab per former page, plus any additional tabs (e.g. "All").
- Delete the old page directories entirely.
- Remove old navigation buttons from all remaining pages (delete the button JSON files).
- Update
application.jsonto remove entries for deleted pages. - Keep one nav button pointing to the surviving page.
Reference
| Item | Value |
|---|---|
| Widget type | TABS_WIDGET version 3 |
| Widget directory | widgets/Tabs1/ |
| Canvas parent | Tabs widgetId |
| Widget-in-tab parent | Canvas widgetId for that tab |
defaultTab |
Tab label (not tab ID) |
tabsObj keys |
tab1, tab2, tab3, ... |
| Existing examples | Sales - Units Shipped, Pending POs |