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.8 KiB
name, description
| name | description |
|---|---|
| add-query-to-table | Adds a new DB query to a page and wires it to a Table widget; documents how parameter/search syntax works (widget bindings, optional date range). Use when the user asks to add a query, connect a query to a table, create a new query, or how parameters/filters work in queries. |
Add Query to Table & Parameter Search Syntax
Use this skill when adding a new query to a page, wiring a query to a table, or when you need the correct syntax for parameterized filters (e.g. date range from widgets).
1. Query structure (single source of truth)
Each query lives under the page in a folder named after the query:
- Path:
pages/<PageName>/queries/<query_name>/ - Files:
<query_name>.txt-- Raw SQL. This is the source of truth for the query body. Keep it readable (real newlines, no escaping).metadata.json-- Appsmith action config. ThebodyinsideunpublishedAction.actionConfigurationmust match the SQL in the.txtfile, stored as a single-line string with\nfor newlines and\"for double quotes.
Naming: Use lowercase with underscores (e.g. units_ordered_by_series, pending_pos_slx_pending). The query name is the identifier used in bindings (e.g. {{query_name.data}}).
2. Metadata.json required fields
gitSyncId(CRITICAL): Must be the first field in the root object. Format:"<24-char-hex>_<uuid-v4>". Without this, Appsmith will auto-remove the query on the next pull/sync. Generate with:Use the same 24-char hex prefix as the page'spython3 -c "import uuid; print(uuid.uuid4().hex[:24] + '_' + str(uuid.uuid4()))"gitSyncIdbut a unique UUID suffix.id:"<PageName>_<query_name>"(e.g."Pending POs - SLx Pending_pending_pos_slx_pending").name:"<query_name>"(must match the folder and the name used in bindings).unpublishedAction.actionConfiguration.body: The exact SQL from<query_name>.txt, escaped for JSON (newlines ->\n,"->\").unpublishedAction.datasource: Use the same as other queries on the page (e.g.xTuple_Sandbox).unpublishedAction.pageId:"<PageName>".unpublishedAction.dynamicBindingPathList:[{"key":"body"}]when the body contains{{...}}widget references.- Keep
pluginId:"postgres-plugin",pluginType:"DB", and existing flags likeencodeParamsToggle,paginationType,pluginSpecifiedTemplates,timeoutInMillisecondconsistent with other DB queries in the app.
When adding a new query, copy an existing query's metadata.json from the same page and change gitSyncId, id, name, and body (and ensure body stays in sync with the new .txt file).
3. Wiring the query to a Table widget
- In the Table widget JSON (e.g.
widgets/.../Table1.json), set:tableData:"{{<query_name>.data}}"
- Table columns read from the query result by key. The key is the SELECT alias from the query (e.g.
"Product","Qty Ordered"). InprimaryColumns, the column id can be a safe identifier (e.g.Qty_Ordered) while originalId / alias / label match the display name; computedValue must use the same key as in the query result, e.g.currentRow["Qty Ordered"].
So: query SELECT aliases = keys in the table's row object. Keep column keys and any currentRow["..."] references in the table in sync with those aliases.
4. Parameter search syntax (widgets in SQL)
4.1 Referencing a widget value
- Syntax:
{{WidgetName.property}} - Examples:
- DatePicker date:
{{SeriesDateFrom.selectedDate}},{{PowerDateTo.selectedDate}} - Other widgets: use the widget's value property (e.g.
selectedOptionValue,text) as per Appsmith docs.
- DatePicker date:
Values are injected as strings. For PostgreSQL dates you typically cast in SQL, e.g. '{{DatePicker1.selectedDate}}'::date.
4.2 Optional date range (no filter when either date is empty)
Use this pattern so that:
- If either date widget is empty -> the date condition is not applied (all dates allowed).
- If both are set -> filter by
date_column BETWEEN from ::date AND to ::date.
SQL pattern (replace widget names and column as needed):
AND (
NULLIF('{{DateFromWidget.selectedDate}}','') IS NULL
OR NULLIF('{{DateToWidget.selectedDate}}','') IS NULL
OR date_column BETWEEN
NULLIF('{{DateFromWidget.selectedDate}}','')::date
AND NULLIF('{{DateToWidget.selectedDate}}','')::date
)
- Logic:
NULLIF('{{...}}','')turns an empty string into SQLNULL. If either widget is empty, one of the first two conditions is true, so the wholeAND (...)is true and the BETWEEN is not applied. When both are non-empty, the third branch applies the range. - Widget names: Use the actual widget names (e.g.
SeriesDateFrom/SeriesDateTofor one tab,PowerDateFrom/PowerDateTofor another). Ensure those widgets exist on the same page and (if in a tab) the same tab so the query runs with the right context.
4.3 Required parameters (always filter)
If the filter must always be applied (no "show all" when empty):
- Use the binding directly and ensure the widget always has a value, or use a default in the widget.
- Example:
AND cohead_orderdate BETWEEN '{{DateFrom.selectedDate}}'::date AND '{{DateTo.selectedDate}}'::date-- then empty dates may produce invalid SQL or empty results, so prefer the optional pattern above unless the UI guarantees non-empty values.
5. Checklist when adding a new query
- Create
pages/<PageName>/queries/<query_name>/. - Add
<query_name>.txtwith the full SQL (source of truth). - Add
metadata.jsonwith correctgitSyncId,id,name,body(body = SQL from .txt, JSON-escaped), and same datasource/pageId as other page queries. - If the SQL uses
{{...}}, setdynamicBindingPathListto[{"key":"body"}]. - In the Table widget that should show the data, set
tableDatato{{<query_name>.data}}. - Ensure table column keys /
currentRow["..."]match the query's SELECT aliases.
6. Renaming or replacing a query
- To rename (e.g.
units_shipped_by_series->units_ordered_by_series):- Create the new folder and files under the new name (with a new
gitSyncId). - Update every reference to the old query (e.g.
tableData:{{old_name.data}}->{{new_name.data}}). - Remove the old query folder (both
.txtandmetadata.json).
- Create the new folder and files under the new name (with a new
- When replacing the SQL but keeping the same name, update the
.txtfirst, then update thebodyinmetadata.jsonto match (same content, JSON-escaped).
Reference
- gitSyncId:
<24-char-hex>_<uuid-v4>. Without this, Appsmith auto-removes the query on sync. - Optional date range: NULLIF + IS NULL + OR + BETWEEN as above.
- Table data binding:
{{<query_name>.data}}. - Column keys: Must match query SELECT aliases (e.g.
"Product","Qty Ordered").