pgkiln 0.45.0 → 0.46.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (71) hide show
  1. package/db/migrations/106_form_designer.sql +102 -0
  2. package/db/migrations/107_app_version.sql +9 -0
  3. package/docs/apex-feature-parity.md +6 -3
  4. package/docs/guide/03-builder.md +36 -12
  5. package/docs/guide/04-pages-and-regions.md +74 -2
  6. package/docs/guide/05-items.md +14 -2
  7. package/docs/guide/09-reference.md +1 -1
  8. package/docs/guide/11-from-apex.md +2 -1
  9. package/docs/guide/12-development.md +6 -1
  10. package/examples/hr/hr_61_form_designer.sql +80 -0
  11. package/package.json +1 -1
  12. package/public/app.css +62 -2
  13. package/public/app.js +244 -35
  14. package/public/builder.css +21 -1
  15. package/public/builder.js +67 -0
  16. package/public/sw.js +2 -1
  17. package/src/app.ts +9 -1
  18. package/src/builder/appwizard.ts +22 -6
  19. package/src/builder/arrange.ts +33 -12
  20. package/src/builder/code-editor.ts +1 -1
  21. package/src/builder/components.ts +1 -1
  22. package/src/builder/designer.ts +44 -17
  23. package/src/builder/errors.ts +65 -0
  24. package/src/builder/form-designer.ts +229 -0
  25. package/src/builder/locks.ts +6 -0
  26. package/src/builder/newapp.ts +2 -1
  27. package/src/builder/object-browser.ts +81 -18
  28. package/src/builder/region-help.ts +2 -0
  29. package/src/builder/region-settings.ts +2 -0
  30. package/src/builder/reporter.ts +1 -1
  31. package/src/builder/routes.ts +7 -5
  32. package/src/builder/search.ts +3 -3
  33. package/src/builder/shared.ts +2 -2
  34. package/src/builder/ui.ts +2 -0
  35. package/src/cli/replace.ts +10 -0
  36. package/src/i18n/ar.ts +62 -0
  37. package/src/i18n/cs.ts +62 -0
  38. package/src/i18n/da.ts +62 -0
  39. package/src/i18n/de.ts +62 -0
  40. package/src/i18n/el.ts +62 -0
  41. package/src/i18n/es.ts +62 -0
  42. package/src/i18n/fi.ts +62 -0
  43. package/src/i18n/fr.ts +62 -0
  44. package/src/i18n/he.ts +62 -0
  45. package/src/i18n/it.ts +62 -0
  46. package/src/i18n/ja.ts +62 -0
  47. package/src/i18n/ko.ts +62 -0
  48. package/src/i18n/nb.ts +62 -0
  49. package/src/i18n/pl.ts +62 -0
  50. package/src/i18n/pt.ts +62 -0
  51. package/src/i18n/ru.ts +62 -0
  52. package/src/i18n/sv.ts +62 -0
  53. package/src/i18n/tr.ts +62 -0
  54. package/src/i18n/uk.ts +62 -0
  55. package/src/i18n/zh.ts +62 -0
  56. package/src/i18n.ts +124 -0
  57. package/src/metadata.ts +4 -2
  58. package/src/runtime/devbar.ts +1 -1
  59. package/src/runtime/facet-state.ts +10 -5
  60. package/src/runtime/facets.ts +29 -5
  61. package/src/runtime/form-designer.ts +676 -0
  62. package/src/runtime/grid.ts +15 -8
  63. package/src/runtime/items.ts +33 -21
  64. package/src/runtime/lists.ts +1 -1
  65. package/src/runtime/pdf.ts +2 -1
  66. package/src/runtime/regions.ts +4 -0
  67. package/src/runtime/render.ts +7 -3
  68. package/src/runtime/report.ts +2 -2
  69. package/src/runtime/routes.ts +3 -1
  70. package/src/runtime/smart-filters.ts +2 -1
  71. package/src/runtime/tasks.ts +6 -3
@@ -0,0 +1,102 @@
1
+ -- ---------------------------------------------------------------------
2
+ -- 106: Form Designer: the application's administrators build their own
3
+ -- data entry forms in the running application.
4
+ --
5
+ -- The developer places a region of type 'form_designer' and lists in its
6
+ -- settings (config.sources) the tables and views it offers: the columns
7
+ -- forms may show and change, the column that identifies a row, and what
8
+ -- forms on it may do at most (add, change, delete rows). Who may design
9
+ -- forms is an authorization scheme (config.design_authz); without one
10
+ -- nobody can. Those settings are part of the region, so they travel with
11
+ -- the application export.
12
+ --
13
+ -- A user form is a definition (source, fields with labels, help, required,
14
+ -- display and default, the columns of its row list, the actions it allows)
15
+ -- under a name, kept per region; published forms are used by everyone who
16
+ -- can see the region, drafts only by designers. User forms are data of the
17
+ -- installation (like meta.data_report): not part of the application export,
18
+ -- kept when an application is replaced. Applications reach the table only
19
+ -- through meta.user_forms and the two functions; the runtime checks every
20
+ -- definition against the region's offered columns before it renders or
21
+ -- saves, and reads and writes rows as the application's database role, so
22
+ -- grants and row level security apply.
23
+ -- ---------------------------------------------------------------------
24
+ alter table meta.region drop constraint region_type_check;
25
+ alter table meta.region add constraint region_type_check
26
+ check (type in ('report', 'form', 'chart', 'cards', 'static', 'grid', 'calendar', 'dynamic', 'facets', 'tasks', 'workflows', 'map', 'tree',
27
+ 'template_component', 'smart_filters', 'display_selector', 'list', 'data_reporter', 'ai_assistant', 'plugin', 'url', 'search',
28
+ 'login', 'form_designer'));
29
+
30
+ create table meta.user_form (
31
+ id serial primary key,
32
+ app_id int not null references meta.app on delete cascade,
33
+ region_id int not null references meta.region on delete cascade,
34
+ name text not null check (length(btrim(name)) between 1 and 80),
35
+ description text check (length(description) <= 500),
36
+ published boolean not null default false,
37
+ definition jsonb not null check (jsonb_typeof(definition) = 'object' and length(definition::text) <= 32000),
38
+ created_by text not null,
39
+ updated_by text not null,
40
+ created_at timestamptz not null default now(),
41
+ updated_at timestamptz not null default now(),
42
+ unique (region_id, name)
43
+ );
44
+ create index user_form_app_idx on meta.user_form (app_id);
45
+ revoke all on meta.user_form from public;
46
+
47
+ create view meta.user_forms with (security_barrier) as
48
+ select id, region_id, name, description, published, definition, created_by, updated_by, created_at, updated_at
49
+ from meta.user_form
50
+ where app_id = meta.app_id();
51
+ grant select on meta.user_forms to public;
52
+
53
+ -- Save a form of a Form Designer region of the current application: a new
54
+ -- one (p_id null) or an existing one (p_id). Returns its id. Whether the
55
+ -- user may design forms (the region's authorization scheme) is checked by
56
+ -- the runtime before it calls this.
57
+ create function meta.save_user_form(p_region int, p_id int, p_name text, p_description text, p_definition jsonb, p_published boolean default false)
58
+ returns int
59
+ language plpgsql security definer set search_path = meta, pg_catalog as $$
60
+ declare
61
+ v_user text := meta.app_user();
62
+ v_app int := meta.app_id();
63
+ v_id int;
64
+ begin
65
+ if v_user is null or v_user = 'nobody' then
66
+ raise exception 'sign in to design forms';
67
+ end if;
68
+ if not exists (select 1 from region r join page p on p.id = r.page_id where r.id = p_region and p.app_id = v_app and r.type = 'form_designer') then
69
+ raise exception 'unknown form designer region %', p_region;
70
+ end if;
71
+ begin
72
+ if p_id is null then
73
+ insert into user_form (app_id, region_id, name, description, published, definition, created_by, updated_by)
74
+ values (v_app, p_region, btrim(p_name), nullif(btrim(p_description), ''), coalesce(p_published, false), p_definition, v_user, v_user)
75
+ returning id into v_id;
76
+ else
77
+ update user_form
78
+ set name = btrim(p_name), description = nullif(btrim(p_description), ''), published = coalesce(p_published, false),
79
+ definition = p_definition, updated_by = v_user, updated_at = now()
80
+ where id = p_id and app_id = v_app and region_id = p_region
81
+ returning id into v_id;
82
+ if v_id is null then
83
+ raise exception 'form % does not exist', p_id;
84
+ end if;
85
+ end if;
86
+ exception when unique_violation then
87
+ raise exception 'There is already a form named "%".', btrim(p_name) using errcode = 'P0001';
88
+ end;
89
+ return v_id;
90
+ end
91
+ $$;
92
+
93
+ -- Delete a form of a Form Designer region of the current application.
94
+ create function meta.delete_user_form(p_region int, p_id int) returns boolean
95
+ language sql security definer set search_path = meta, pg_catalog as $$
96
+ with d as (delete from user_form where id = p_id and region_id = p_region and app_id = meta.app_id()
97
+ and meta.app_user() is not null and meta.app_user() <> 'nobody' returning 1)
98
+ select exists (select 1 from d)
99
+ $$;
100
+
101
+ revoke all on function meta.save_user_form(int, int, text, text, jsonb, boolean), meta.delete_user_form(int, int) from public;
102
+ grant execute on function meta.save_user_form(int, int, text, text, jsonb, boolean), meta.delete_user_form(int, int) to public;
@@ -0,0 +1,9 @@
1
+ -- ---------------------------------------------------------------------
2
+ -- 107: an application's version (APEX: Application Definition → Version,
3
+ -- "Release 1.0"), shown in the footer of its pages with "Back to top".
4
+ -- New applications start with 'Release 1.0'; existing ones have none (no
5
+ -- version in the footer) until a developer sets one in Settings. It travels
6
+ -- with the export like the other columns of meta.app.
7
+ -- ---------------------------------------------------------------------
8
+ alter table meta.app add column version text check (length(version) <= 40);
9
+ comment on column meta.app.version is 'The application''s version, e.g. Release 1.0: shown in its pages'' footer (107)';
@@ -7,7 +7,7 @@ open item and open an issue or pull request; see [CONTRIBUTING.md](../CONTRIBUTI
7
7
  Legend: ✅ available · 🟡 partial (see notes) · ❌ not yet · ➖ not planned (a deliberate choice,
8
8
  or better served by the PostgreSQL ecosystem; see the notes and [extensions](guide/15-extensions.md))
9
9
 
10
- Last reviewed: 2026-10-15 (sprint 83: Component Settings and Component Groups; sprint 82: Report Queries; sprint 81: Application Computations and Shortcuts in Shared Components; sprint 80: the master grid's first row selected when the page opens and after a search; grid cells as text with their format mask until edited, with cell navigation by arrow keys, Enter, F2 and Escape; sprint 79: generated dashboards with sums, donuts and lines, leaving out charts that say nothing; applications open in their own light or dark mode until the user chooses, per application; a user menu with Appearance and Sign in when signed out; the developer toolbar's App and Page texts and debug state; the report search's column scope in the magnifier; sprint 78: generated faceted search reports with one column per foreign key, the faceted search's total row count, Show all and Yes/No values, one search field, friendly headings and number formats in generated grids and master-detail pages, friendly names in the Create Application wizard's page list; sprint 77: the Page Designer's layout and tree by display position, button positions, the tree's context menu with Duplicate and Copy to other page, Show Common / Show All, Go to group and the Template Options dialog; display positions, button positions and side-column page templates at runtime; sprint 76: the interactive report's menus and pagination, date items in the date format; sprint 75: the SQL Workshop's Object Browser and tabs; sprint 74: the builder's information architecture: Shared Components grid, Settings tabs, application home search and tasks; sprint 73: the Create Application wizard on one page with page types and features; sprint 72: the runtime chrome compared with APEX 26.2's: the developer toolbar, the Iris look, the navigation bar's Help menu and Install button; sprint 71: generated applications compared with APEX 26.2's: labels, foreign key names in reports, file columns, format masks, the Home page and the dashboard; sprint 70: treemap, sunburst and timeline charts; sprint 69: dragging tasks on a Gantt chart; sprint 68: the Data Reporter's welcome page, Find my workspace; sprint 67: REST source catalogs from OpenAPI documents, a workspace's export and import (two rows added); sprint 66: a blueprint from an existing application; sprint 65: facet charts; sprint 64: approval vacation rules and expiration policies; sprint 63: range, box plot and stock charts; sprint 62: workspace preferences (SQL Workshop and RESTful services per workspace), extension links, Manage backups across applications, SQL Workshop utilities with object reports, About Database and a database monitor; sprint 61: the builder's screens compared with APEX 26.1: the workspace sign-in page, the Administration menu with workspace message, environment banner and utilization, the SQL Workshop home, the App Gallery, application groups and cross application reports (four rows added); sprint 60: isolated workspaces with their own database logins, row-level security on pgkiln's tables and workspace database roles, workspace administrators; sprint 59: the Page Designer's help pane, Quick edit from the developer toolbar; sprint 58: page groups; sprint 57: region templates hero, title bar, buttons container, inline dialog and drawer, wizard with wizard progress, the carousel; sprint 56: Generate DDL and Schema Comparison in the SQL Workshop, `pgkiln ddl` and `pgkiln compare`; sprint 55: an editable login page with a Login region and the Minimal page template, Forgot password through the application's mail service; sprint 54: interactive grid views, the single row view, group by and chart; sprint 53: application settings, user preferences and items kept per user, dynamic translations, `meta.data_export` and data export downloads, display image and geocoded address items; sprint 52: every dynamic action event and action, Ajax callbacks, an error handling function, the session timeout warning; sprint 51: checked imports, application backups, availability and Run only, feedback, type-specific attribute help; sprint 50: lost update detection, warn on unsaved changes, the URL and search regions with search configurations, the developer toolbar with a session state viewer, and e-mail through SMTP with OAuth2, Microsoft Graph, Amazon SES and HTTP APIs; import checks, application availability, property help and e-mail compared again; 2026-10-06: pgkiln 0.31.0, in progress: static application files and the Execute JavaScript dynamic action; region, item, dynamic action and process plug-ins; conditional and dynamic theme styles; XML and Excel in `meta.parse_data`, zips in SQL, `meta.v_boolean`; file items in S3-compatible object storage; map layers by the visible area and as vector tiles; a graphical query builder; tenants for workflows and tasks; application files as YAML and region static IDs; Lucide icons and icon modifiers; PWA push notifications; 0.30.0: 136 icons and an icon picker; an axe-core accessibility audit; cropping pictures; session sharing between applications; twenty-two built-in languages; 0.29.0: workspaces; the Iris base style; drawers; built-in template components including the metric card; Theme Roller dark-mode colours, live preview and template options on items and report columns; report selection across pages; instance settings; fourteen built-in languages; 0.28.0: AI with Claude and OpenAI; 0.27.0: Data Reporter; sample data generator; create application from several sheets, pasted data or existing tables; 0.26.0: working copies with a three-way merge; theme, library and boilerplate applications with subscriptions; 0.25.0: Gantt, pyramid and polar charts; map layers, clustering and server-side spatial filters; REST data sources that write back from forms and grids and synchronise into tables, OAuth2 password and refresh-token grants; debug messages per request and an install/upgrade log; `meta.web_request()` and `meta.parse_data()`; Theme Roller style variants and template options; 0.24.0: automations with several actions, error handling per row and runs from SQL; invoke-API workflow steps; Unload Data in the Data Workshop; create page wizards for cards, calendar, chart, map, faceted search, form and master-detail; create an application from a file; 0.23.0: interactive grid aggregates, frozen/moved/resized columns, layouts and saved grid reports, master-detail, row actions and copy/paste; download processes, execution chains in the background, workflow processes, branches to a function's URL or another app, the Dialog Closed event; number format masks, automatic time zone, runtime messages in German, French and Spanish; SQL scripts, Quick SQL, a query builder, XML loading and data load definitions; custom authentication, lists, page and application locks with developer comments, supporting objects; 0.22.0: keyset paging, report PDFs and REST collections streamed from a cursor, database-account authentication; 0.21.0: HTTP-header authentication behind a reverse proxy; 0.20.0: a popup list of values with server-side search; 0.19.0: row-range pagination, maximum row counts and row limits, lazy loading and caching of regions, streamed CSV/Excel downloads; 0.18.0: calendar week/day/list views with drag and drop, bubble/gauge/funnel/radar charts and drill-down, smart filters, a region display selector and more facet types, computations, conditional branches, build options, menu buttons, REST data sources and web credentials, rich text/Markdown, rating, combobox, date range and QR code items; 0.17.0: an App Builder home, workspace dashboard and utilities like APEX's; 0.16.0: a Page Designer with drag-and-drop layout, a code editor with autocomplete, template components and plug-ins, parallel branches and versions of workflows, the `pgkiln` command line with one file per component; 0.15.0: drop and paste files, heat maps, filtering a report by the map area; 0.14.0: stacked, combo, scatter and pie charts, several files per upload item; 0.13.0: map and tree regions; 0.12.0: REST modules in the builder; 0.11.0: workflows and Progressive Web Apps; 0.10.0: LDAP, SAML, "Keep me signed in", document templates, JSON loading, approvals and the task list; pgkiln installs no application, HR is an example).
10
+ Last reviewed: 2026-10-15 (sprint 90: Comment Out in the page designer, gallery labels in title case; sprint 89: the Object Browser's schema, one row of tabs, blank NULLs, Modify column; sprint 88: home cards with colour bands and round icons, the hero on the page background, a page footer with the version and Back to top; sprint 87: the Create Application wizard reloads the page list for a schema, RLS badges, the alias from the name; sprint 86: the page designer by page number, HTML error pages in the builder; sprint 85: the Form Designer, beyond APEX; sprint 84: the date picker, facet menus; sprint 83: Component Settings and Component Groups; sprint 82: Report Queries; sprint 81: Application Computations and Shortcuts in Shared Components; sprint 80: the master grid's first row selected when the page opens and after a search; grid cells as text with their format mask until edited, with cell navigation by arrow keys, Enter, F2 and Escape; sprint 79: generated dashboards with sums, donuts and lines, leaving out charts that say nothing; applications open in their own light or dark mode until the user chooses, per application; a user menu with Appearance and Sign in when signed out; the developer toolbar's App and Page texts and debug state; the report search's column scope in the magnifier; sprint 78: generated faceted search reports with one column per foreign key, the faceted search's total row count, Show all and Yes/No values, one search field, friendly headings and number formats in generated grids and master-detail pages, friendly names in the Create Application wizard's page list; sprint 77: the Page Designer's layout and tree by display position, button positions, the tree's context menu with Duplicate and Copy to other page, Show Common / Show All, Go to group and the Template Options dialog; display positions, button positions and side-column page templates at runtime; sprint 76: the interactive report's menus and pagination, date items in the date format; sprint 75: the SQL Workshop's Object Browser and tabs; sprint 74: the builder's information architecture: Shared Components grid, Settings tabs, application home search and tasks; sprint 73: the Create Application wizard on one page with page types and features; sprint 72: the runtime chrome compared with APEX 26.2's: the developer toolbar, the Iris look, the navigation bar's Help menu and Install button; sprint 71: generated applications compared with APEX 26.2's: labels, foreign key names in reports, file columns, format masks, the Home page and the dashboard; sprint 70: treemap, sunburst and timeline charts; sprint 69: dragging tasks on a Gantt chart; sprint 68: the Data Reporter's welcome page, Find my workspace; sprint 67: REST source catalogs from OpenAPI documents, a workspace's export and import (two rows added); sprint 66: a blueprint from an existing application; sprint 65: facet charts; sprint 64: approval vacation rules and expiration policies; sprint 63: range, box plot and stock charts; sprint 62: workspace preferences (SQL Workshop and RESTful services per workspace), extension links, Manage backups across applications, SQL Workshop utilities with object reports, About Database and a database monitor; sprint 61: the builder's screens compared with APEX 26.1: the workspace sign-in page, the Administration menu with workspace message, environment banner and utilization, the SQL Workshop home, the App Gallery, application groups and cross application reports (four rows added); sprint 60: isolated workspaces with their own database logins, row-level security on pgkiln's tables and workspace database roles, workspace administrators; sprint 59: the Page Designer's help pane, Quick edit from the developer toolbar; sprint 58: page groups; sprint 57: region templates hero, title bar, buttons container, inline dialog and drawer, wizard with wizard progress, the carousel; sprint 56: Generate DDL and Schema Comparison in the SQL Workshop, `pgkiln ddl` and `pgkiln compare`; sprint 55: an editable login page with a Login region and the Minimal page template, Forgot password through the application's mail service; sprint 54: interactive grid views, the single row view, group by and chart; sprint 53: application settings, user preferences and items kept per user, dynamic translations, `meta.data_export` and data export downloads, display image and geocoded address items; sprint 52: every dynamic action event and action, Ajax callbacks, an error handling function, the session timeout warning; sprint 51: checked imports, application backups, availability and Run only, feedback, type-specific attribute help; sprint 50: lost update detection, warn on unsaved changes, the URL and search regions with search configurations, the developer toolbar with a session state viewer, and e-mail through SMTP with OAuth2, Microsoft Graph, Amazon SES and HTTP APIs; import checks, application availability, property help and e-mail compared again; 2026-10-06: pgkiln 0.31.0, in progress: static application files and the Execute JavaScript dynamic action; region, item, dynamic action and process plug-ins; conditional and dynamic theme styles; XML and Excel in `meta.parse_data`, zips in SQL, `meta.v_boolean`; file items in S3-compatible object storage; map layers by the visible area and as vector tiles; a graphical query builder; tenants for workflows and tasks; application files as YAML and region static IDs; Lucide icons and icon modifiers; PWA push notifications; 0.30.0: 136 icons and an icon picker; an axe-core accessibility audit; cropping pictures; session sharing between applications; twenty-two built-in languages; 0.29.0: workspaces; the Iris base style; drawers; built-in template components including the metric card; Theme Roller dark-mode colours, live preview and template options on items and report columns; report selection across pages; instance settings; fourteen built-in languages; 0.28.0: AI with Claude and OpenAI; 0.27.0: Data Reporter; sample data generator; create application from several sheets, pasted data or existing tables; 0.26.0: working copies with a three-way merge; theme, library and boilerplate applications with subscriptions; 0.25.0: Gantt, pyramid and polar charts; map layers, clustering and server-side spatial filters; REST data sources that write back from forms and grids and synchronise into tables, OAuth2 password and refresh-token grants; debug messages per request and an install/upgrade log; `meta.web_request()` and `meta.parse_data()`; Theme Roller style variants and template options; 0.24.0: automations with several actions, error handling per row and runs from SQL; invoke-API workflow steps; Unload Data in the Data Workshop; create page wizards for cards, calendar, chart, map, faceted search, form and master-detail; create an application from a file; 0.23.0: interactive grid aggregates, frozen/moved/resized columns, layouts and saved grid reports, master-detail, row actions and copy/paste; download processes, execution chains in the background, workflow processes, branches to a function's URL or another app, the Dialog Closed event; number format masks, automatic time zone, runtime messages in German, French and Spanish; SQL scripts, Quick SQL, a query builder, XML loading and data load definitions; custom authentication, lists, page and application locks with developer comments, supporting objects; 0.22.0: keyset paging, report PDFs and REST collections streamed from a cursor, database-account authentication; 0.21.0: HTTP-header authentication behind a reverse proxy; 0.20.0: a popup list of values with server-side search; 0.19.0: row-range pagination, maximum row counts and row limits, lazy loading and caching of regions, streamed CSV/Excel downloads; 0.18.0: calendar week/day/list views with drag and drop, bubble/gauge/funnel/radar charts and drill-down, smart filters, a region display selector and more facet types, computations, conditional branches, build options, menu buttons, REST data sources and web credentials, rich text/Markdown, rating, combobox, date range and QR code items; 0.17.0: an App Builder home, workspace dashboard and utilities like APEX's; 0.16.0: a Page Designer with drag-and-drop layout, a code editor with autocomplete, template components and plug-ins, parallel branches and versions of workflows, the `pgkiln` command line with one file per component; 0.15.0: drop and paste files, heat maps, filtering a report by the map area; 0.14.0: stacked, combo, scatter and pie charts, several files per upload item; 0.13.0: map and tree regions; 0.12.0: REST modules in the builder; 0.11.0: workflows and Progressive Web Apps; 0.10.0: LDAP, SAML, "Keep me signed in", document templates, JSON loading, approvals and the task list; pgkiln installs no application, HR is an example).
11
11
 
12
12
  ## At a glance
13
13
 
@@ -38,7 +38,7 @@ two, REST source catalogs and the workspace export, with their features.)
38
38
  | App Builder: create, edit, delete, run apps | ✅ | Builder at `/builder` |
39
39
  | Create application wizard | ✅ | Blank app with its own database role and schema, started from a **boilerplate application** if wanted; **from a file**: CSV/TSV/XLSX/JSON/XML, where an Excel workbook with several sheets (or JSON with several arrays) becomes several tables, with a preview to include or leave out each sheet, edit table/column names, types and primary keys, and **foreign keys proposed** from matching columns; **from pasted data** (CSV/TSV text); **from existing tables** (pick the tables and views of a schema). Each gives a report and form per table, navigation and an optional dashboard (a chart per table), plus an optional faceted search for a single table, all created in one transaction. (sprint 73) **One page like APEX's**: name, icon and description, appearance (theme style, side or top menu, accent), a **page type per table** (interactive report with form, faceted search, cards, calendar, master detail, interactive grid, report, none; large tables proposed as a grid), **features** (About page, access control roles, activity reporting, feedback, theme style selection, PWA, push), settings (alias, authentication, first user, language) and a boilerplate under Advanced; a progress message, then straight to the new application. (sprint 71) The generated pages read like APEX's: human labels (abbreviations, run-together words, column and table comments), plural page and menu names with singular form titles, foreign keys shown by the parent's name in reports, `bytea` columns as one file item with their name/mime/size/updated columns, number format masks, a pencil per report row, the Dashboard after Home with *(none)* and top-N charts, and a Home page with a hero and a card per page. **Missing:** blueprints (26.1) |
40
40
  | Create page wizards | ✅ | Report and form, interactive grid, form, cards, calendar, chart, map, faceted search and master-detail (stacked grids) from any table or view, with defaults from the catalog (columns, key, dates, positions, foreign keys), an optional modal form page and a navigation entry; also from SQL (`meta.generate_page`). No side-by-side or drill-down master-detail, no wizard for smart filters or trees |
41
- | Page Designer | ✅ | IDE-style window (dark or light): component tree, a layout canvas with drag and drop (with keyboard and button alternatives) and a gallery of regions, items and buttons, a filterable property editor; undo/redo; panes become tabs on phones. Settings forms for report, grid, chart, cards, calendar, faceted search, smart filters, display selector, map, tree and template component regions. A code editor for SQL, PL/pgSQL, JSON and HTML with highlighting and autocomplete of tables, columns and `:ITEM` binds (no extra libraries). Drag and drop needs a mouse; on touch screens the Arrange buttons move components. (sprint 77) The layout draws the page template's display positions (banner, breadcrumb bar, left/right column, body, footer) as drop zones and a region's button positions; the rendering tree groups by position and has a context menu (create in or below, Duplicate, Copy to other page, expand/collapse all, delete); the property editor has Common / All, Go to group and a Template Options dialog; a page number in the toolbar. No nested regions (APEX's sub regions) and no Comment Out |
41
+ | Page Designer | ✅ | IDE-style window (dark or light): component tree, a layout canvas with drag and drop (with keyboard and button alternatives) and a gallery of regions, items and buttons, a filterable property editor; undo/redo; panes become tabs on phones. Settings forms for report, grid, chart, cards, calendar, faceted search, smart filters, display selector, map, tree and template component regions. A code editor for SQL, PL/pgSQL, JSON and HTML with highlighting and autocomplete of tables, columns and `:ITEM` binds (no extra libraries). Drag and drop needs a mouse; on touch screens the Arrange buttons move components. (sprint 77) The layout draws the page template's display positions (banner, breadcrumb bar, left/right column, body, footer) as drop zones and a region's button positions; the rendering tree groups by position and has a context menu (create in or below, Duplicate, Copy to other page, expand/collapse all, (sprint 90) Comment out with a COMMENTED_OUT build option, delete; no Create Sub Region: no nested regions); the property editor has Common / All, Go to group and a Template Options dialog; a page number in the toolbar. No nested regions (APEX's sub regions) and no Comment Out |
42
42
  | Shared components | ✅ | (sprint 74) A grid of ten groups with counts like APEX's, a searchable, sortable list per type with Create, and Settings-backed types linking to their Settings tab ([mapping below](#shared-components-apex-links-and-pgkiln)). Navigation menu, **lists** (static entries with nesting, badges, conditions, authorization and build options, or a query; for list regions, the navigation menu and the navigation bar), authorization schemes, build options, lists of values, application items and processes, access control, globalization, template components and plug-ins, web credentials, REST data sources, data load definitions, supporting objects |
43
43
  | Export / import | ✅ | `meta.export_app()` / `meta.import_app()`: portable JSON, also in the builder (new applications; `pgkiln import --replace` and the MCP `import_app` update one in place, live for every user at the commit). (sprint 51) **Checked imports**: `pgkiln import --check` (`--dry-run`, `--force`), the MCP import (by default) and the builder's import run the Advisor inside the import's transaction (SQL planned as the application's role: missing tables, columns, functions, types, grants; references) and refuse on errors, so nothing changes; APEX doesn't check. The version an import replaces is [backed up](guide/03-builder.md#backups) first ([chapter 18](guide/18-cli.md#checking-before-an-import)) |
44
44
  | APEXlang: human-readable, diffable app files; static IDs (26.1) | ✅ | `pgkiln export --format dir`: one JSON file per component with sorted keys, SQL and HTML in sibling files, references by static id instead of database ids; `pgkiln import --replace` updates an app in place, `pgkiln diff` compares ([chapter 18](guide/18-cli.md)). (0.31) **`--format text`**: the same files as YAML (a strict subset any YAML tool reads) with the SQL and templates inline, read back losslessly ([text files](guide/18-cli.md#text-files-apexlang)); regions have a stored **Static ID** (exported as their key, rendered as `data-static-id`), other components are named by their stored names. The syntax is YAML rather than APEXlang's own, so APEXlang files don't import |
@@ -176,7 +176,7 @@ two, REST source catalogs and the workspace export, with their features.)
176
176
  | Translated applications (26.1: text-message-based translation of one app) | ✅ | One app with translations, like 26.1; XLIFF 1.2 and CSV export/import, coverage per language |
177
177
  | Text messages (`APEX_LANG.MESSAGE`, `&APP_TEXT$…`) | ✅ | `meta.message()`, `&APP_TEXT$NAME.`, fallback to the base and primary language |
178
178
  | Language from browser, preference or session | ✅ | Browser, user preference or primary; `?lang=`; right-to-left languages |
179
- | Date and timestamp format masks | ✅ | Per app or per language; (sprint 76) date items show and read the application's date format (or their own) with a calendar button |
179
+ | Date and timestamp format masks | ✅ | Per app or per language; (sprint 76) date items show and read the application's date format (or their own) with a calendar button; (sprint 84) pgkiln's date picker (keyboard, week start, Today, Clear) for date items, date ranges, grid date cells and facet date ranges; timestamps keep the browser's field |
180
180
  | Dynamic translations (`APEX_LANG.LANG`: translated data values, e.g. list of values entries) | ✅ | (sprint 53) Shared Components → Dynamic translations; `meta.lang(text)` in a query returns the value in the session's language (base language fallback); exported with the application |
181
181
  | Number format masks, automatic time zone | ✅ | Oracle-style number masks (`999G999G990D00`, `FML…`, `0000`, `%`, `S`/`MI`/`PR`, `EEEE`, `X`, `RN`) on report, grid and cards columns, charts and number/display items, in the language's separators with the app's currency; Automatic Time Zone (browser, My account, app or database), `timestamptz` shown in it ([chapter 14](guide/14-globalization.md)) |
182
182
 
@@ -269,6 +269,9 @@ Small but real differences, for teams comparing the two:
269
269
  - An import can be checked before it commits (`pgkiln import --check`): SQL that names a missing
270
270
  table or column refuses the deployment instead of failing at run time.
271
271
  - E-mail through SMTP with OAuth2, Microsoft Graph, Amazon SES and HTTP APIs, not only SMTP with a password.
272
+ - (sprint 85) A **Form Designer** region ([chapter 4](guide/04-pages-and-regions.md#form_designer-form-designer)): the
273
+ application's administrators build and publish data entry forms in the running application, on the tables and
274
+ columns the developer offers, with grants, row level security and lost update detection applying.
272
275
 
273
276
  ## Roadmap (proposed priority)
274
277
 
@@ -199,6 +199,11 @@ Every builder page has the same frame, modelled on an IDE:
199
199
 
200
200
  On phones the rail gets narrower and the breadcrumb shows only the current page.
201
201
 
202
+ (sprint 86) An address under `/builder` that leads nowhere (a deleted application or page, a typo, an
203
+ application of a workspace you are not a member of) shows a **Page not found** page in the builder's
204
+ theme with a link to the App Builder home; a server error shows **Something went wrong** (the error
205
+ is in the server log, not on the page). Scripts get the plain answers they always got.
206
+
202
207
  ## App Builder home
203
208
 
204
209
  The home page is laid out like APEX's App Builder:
@@ -251,15 +256,15 @@ start from a file, pasted data, a blueprint or the App Gallery instead. Then:
251
256
 
252
257
  | Section | Field | Meaning |
253
258
  |---|---|---|
254
- | Data | Pages on the tables of | The parsing schema: an existing schema (*Show its tables* lists them under Pages), or a new, empty one named after the alias |
259
+ | Data | Pages on the tables of | The parsing schema: an existing schema, or a new, empty one named after the alias. (sprint 87) Choosing a schema lists its tables under Pages at once, with the name and alias proposed for it (unless you already typed them); without JavaScript, *Show its tables* does it |
255
260
  | Application | Name | Display name, shown in the header. Proposed from the schema and selected when you click it, so typing replaces it |
256
261
  | | Description, Icon | Shown on the home page's hero and the About page |
257
262
  | Appearance | Theme style, Navigation, Accent colour | Iris, Lavender or Standard; a side or top menu; an own accent colour |
258
- | Pages | a page type per table | *Interactive report with form*, *Faceted search*, *Cards*, *Calendar* (a table with a date column), *Master detail* (a table another table refers to), *Interactive grid*, *Report* or *No page*. Tables with a single-column primary key get a modal form with their report, faceted search, cards and calendar; views and tables without such a key can't have a form or a grid. Proposed: a report with form, an interactive grid for a table of more than 100,000 rows, a report for a view. *Dashboard*: a chart per table |
263
+ | Pages | a page type per table | *Interactive report with form*, *Faceted search*, *Cards*, *Calendar* (a table with a date column), *Master detail* (a table another table refers to), *Interactive grid*, *Report* or *No page*. Tables with a single-column primary key get a modal form with their report, faceted search, cards and calendar; views and tables without such a key can't have a form or a grid. Proposed: a report with form, an interactive grid for a table of more than 100,000 rows, a report for a view. (sprint 87) A table with [row level security](08-security.md) shows *RLS · n* (its number of policies), or *RLS, no policies* in red: the application's role would see no rows. *Dashboard*: a chart per table |
259
264
  | Features | | *About page* (in a Help menu), *Access control* (authorization schemes `CONTRIBUTOR` and `READER` next to `ADMIN`, for the roles admin, contributor and reader), *Activity reporting* (page 9010 for administrators: page views per day and the latest activity, from `meta.app_activity`), *Feedback*, *Theme style selection* (users may choose light or dark), *Install as an app* (PWA) and *Push notifications* (needs `PGKILN_SECRET_KEY`) |
260
- | Settings | Alias | Lowercase URL name: `inventory` gives `/a/inventory` |
265
+ | Settings | Alias | Lowercase URL name: `inventory` gives `/a/inventory`. (sprint 87) It follows the name as you type it (*Café Orders* → `cafe-orders`) until you change it yourself |
261
266
  | | Authentication | *App users* (a login page and a user list), *HTTP header* (a trusted reverse proxy names the user, see [chapter 8](08-security.md#http-header-authentication-reverse-proxy)), *Database accounts* (PostgreSQL login roles and their passwords, see [chapter 8](08-security.md#database-accounts-postgresql-roles)), *Custom* (your own PL/pgSQL function, see [chapter 8](08-security.md#custom-authentication-a-plpgsql-function)) or *None* (a public app) |
262
- | | First user / Password | The first user; they get the `admin` role. An existing account is reused (its password isn't changed) |
267
+ | | First user / Password | The first user; they get the `admin` role. An existing account is reused (its password isn't changed). (sprint 87) Hidden while Authentication is *None* |
263
268
  | | Language | The application's primary language |
264
269
  | Advanced | Start from | Shown when there are **boilerplate** applications (Settings → Application type): their pages, shared components and settings are copied into the new application, which keeps its own name, alias, role and authentication ([application types](#application-types-and-subscriptions)) |
265
270
 
@@ -268,7 +273,9 @@ Creating the app also:
268
273
  - creates the database role `app_<alias>` with `SELECT/INSERT/UPDATE/DELETE` on all tables,
269
274
  `USAGE` on sequences and `EXECUTE` on functions in the schema, plus **default privileges**,
270
275
  so tables you create later (as the owner, e.g. in the SQL Workshop) are usable by the app straight away;
271
- - creates page 1 *Home*, a navigation entry, and an authorization scheme `ADMIN` (role `admin`);
276
+ - creates page 1 *Home*, a navigation entry, and an authorization scheme `ADMIN` (role `admin`); (sprint 88) the home
277
+ page is a hero with the name and icon on the page background and a card per page, each with a colour band and its
278
+ icon in a round badge; the version is *Release 1.0*;
272
279
  - shows a progress message while it works and then opens the new application's page in the builder,
273
280
  with what was created (and anything left out) as a message.
274
281
 
@@ -539,6 +546,11 @@ its pages in four groups.
539
546
 
540
547
  ## The page designer
541
548
 
549
+ (sprint 86) A page's address in the page designer is its application's id and its number:
550
+ `/builder/apps/<app id>/pages/<page number>`, with `?c=region-<id>` (and so on) for the selected
551
+ component, so a link names the page you mean. The older `/builder/pages/<internal id>` still works:
552
+ a browser that opens it is sent to the page's number.
553
+
542
554
  The page designer has three panes, as in APEX's Page Designer. From 1024 pixels wide they sit
543
555
  side by side; on phones and portrait tablets they are tabs (**Tree**, **Layout**, **Properties**):
544
556
  choosing a component opens the Properties tab.
@@ -567,8 +579,12 @@ choosing a component opens the Properties tab.
567
579
  *Create dynamic action*, *Create region below*, **Duplicate** (a copy right after it; a region comes with its
568
580
  items and buttons, names get `_2`), **Copy to other page…** (to the end of a page of the same application; items
569
581
  named `P<n>_` get that page's number, a region's buttons and items come along, references in SQL are not changed),
570
- *Expand all below*, *Collapse all below* and *Delete*. The arrow keys move through the menu, Escape closes it.
571
- APEX's *Create Sub Region* (pgkiln has no nested regions) and *Comment Out* (use a build option) are not there.
582
+ *Expand all below*, *Collapse all below*, (sprint 90) **Comment out** and *Delete*. The arrow keys move through the
583
+ menu, Escape closes it. **Comment out** (APEX's) leaves a component (a region, item, button, dynamic action,
584
+ validation, process, computation or branch) out of the page without deleting it: it gets the
585
+ [build option](#shared-components) `COMMENTED_OUT`, made with the status *Exclude* when first needed, and is struck
586
+ through in the tree; **Uncomment** takes it off again (another build option of the component is left alone).
587
+ APEX's *Create Sub Region* is not there: pgkiln has no nested regions.
572
588
  - **Middle: the Layout**: the page template's display positions as labelled zones (Banner, Breadcrumb bar,
573
589
  the side column of a side-column template beside the Body, Footer), each with its regions on the 12-column
574
590
  grid, each region with its items and its buttons in their button positions, as they will be placed on the page; under it a **gallery** of region types, item types
@@ -692,6 +708,7 @@ out, other keys are kept, columns the query no longer returns stay listed so you
692
708
  | `ai_assistant` | The AI service (those the application may use), system prompt, welcome text, placeholder, error message, tool rounds per question, questions per conversation, public or not, and the context queries and tools as JSON (checked when saving: query shapes, parameter types, REST data sources and authorization schemes that exist) ([AI assistant](04-pages-and-regions.md#ai_assistant-ai-assistant)) |
693
709
  | `report` (also) | *Ask in your own words (AI)*: the AI service and placeholder of the [natural-language filters](04-pages-and-regions.md#natural-language-filters-ask-in-your-own-words) |
694
710
  | `data_reporter` | Data sources (a table or view each: static id, label, description, offered columns with labels and format masks), who may share reports, rows per page ([Data Reporter](04-pages-and-regions.md#data_reporter-data-reporter)) |
711
+ | `form_designer` | Who may design forms (an authorization scheme), data sources (a table or view each: static id, label, description, key column, may add / change / delete rows, offered columns with labels and lists of values), rows per page ([Form Designer](04-pages-and-regions.md#form_designer-form-designer)) |
695
712
 
696
713
  Links, lists of values and the facets' report are checked when saving: a form can only point to
697
714
  pages and shared lists of values of the same application, and to report regions on the same page.
@@ -1011,7 +1028,9 @@ on Definition (`?tab=…` opens another). *Save settings* saves the first five t
1011
1028
  you were on. The Security tab shows only the fields of the chosen authentication type (HTTP header, database
1012
1029
  accounts or custom).
1013
1030
 
1014
- - **Application**: name, alias, home page.
1031
+ - **Application**: name, alias, home page, and (sprint 88) the **version** (APEX: Version, e.g. *Release 1.0*): shown
1032
+ at the bottom of the application's pages, next to *Back to top*. New applications start at *Release 1.0*; empty: no
1033
+ version in the footer.
1015
1034
  - **Security**: authentication (*App users*, *HTTP header*, *Database accounts*, *Custom* or
1016
1035
  *None*), the database role, and **debug mode** (shows full database errors to users; development
1017
1036
  only). The state of the [debug messages](06-processing.md#debug-messages) is shown below it,
@@ -1241,17 +1260,22 @@ others (Quick SQL, Query Builder, Load and Unload Data, Sample Data, DDL, Compar
1241
1260
  statements are allowed; the result of the last one is shown (up to 500 rows). Press
1242
1261
  **Ctrl/Cmd + Enter** to run. The [code editor](#the-code-editor) suggests every schema, table,
1243
1262
  column and function the owner sees.
1244
- - **Object Browser** (APEX's): the **schema** in the header (the session's schema by default) and a tree with a
1263
+ - **Object Browser** (APEX's): the **schema** in the header and a tree with a
1245
1264
  folder per object type (tables, views, materialized views, indexes, sequences, types, functions, procedures,
1246
1265
  triggers) and a **filter** by name. pgkiln's own schemas (`meta`, `pgkiln_plugins`) and objects that belong to an
1247
1266
  extension show only with **Show system objects**. **Create** (and the tiles shown when nothing is selected) opens
1248
1267
  SQL Commands with a CREATE TABLE, VIEW, FUNCTION, SEQUENCE or INDEX statement to fill in. A table opens in tabs:
1249
- **Columns** (with comments; *Add*, *Rename* and *Drop column* write the ALTER TABLE statement into SQL Commands to
1250
- review and run: nothing changes until you press Run), **Data** (25 rows a page), **Indexes**, **Constraints**
1268
+ **Columns** (with comments; the toolbar's *Add*, *Modify* (type, nulls, default), *Rename* and *Drop column* write
1269
+ the ALTER TABLE statement into SQL Commands to review and run: nothing changes until you press Run), **Data** (25
1270
+ rows a page; NULL is a blank cell), **Indexes**, **Constraints**
1251
1271
  (primary key, foreign keys with the table they refer to, unique, check), **Triggers**, **Grants**, **Row level
1252
1272
  security** (on or off, the policies), **Dependencies** (views and foreign keys that use it, and what it uses),
1253
1273
  **Statistics** (rows, sizes, analyze and vacuum times) and **DDL**. A view has the same tabs where they apply; a
1254
- function or procedure shows its source, a trigger, index, sequence or type its definition.
1274
+ function or procedure shows its source, a trigger, index, sequence or type its definition. (sprint 89) The tabs stay
1275
+ on one row: Columns, Data, Indexes, Constraints and DDL (and the current one), the others under **More**. The
1276
+ schema shown is the one you chose last (remembered for the session), else the session's schema of SQL Commands,
1277
+ else the **parsing schema** of the application changed last in your workspace (the schema its database role
1278
+ uses).
1255
1279
  - **SQL Scripts**: saved scripts, shared by the developers of the workspace. Create one in the editor, upload a
1256
1280
  `.sql` file (UTF-8, up to 5 MB) or save one from Quick SQL; download it again as `.sql`. **Run**
1257
1281
  splits the script into statements at semicolons (outside strings, comments, quoted identifiers,
@@ -819,7 +819,9 @@ Counts take the search and all *other* facets into account. Three kinds of facet
819
819
 
820
820
  - **checkbox** (the default): the most frequent values of a column, each with a checkbox; after the seventh
821
821
  the rest sit under *Show all (n)* (open when one of them is chosen), and a boolean's values read *Yes* and
822
- *No*. With
822
+ *No*. Its ⋮ menu (APEX: the facet's menu) has *Clear* (when values are chosen) and *Sort by count* (the
823
+ default) or *Sort by name*; the order is a URL parameter (`r<report id>_fo_<column>=name`) that Apply and
824
+ Reset keep. With
823
825
  `"exclude": true` the facet gets an *Exclude the selected values* switch: the chosen values are
824
826
  then left out instead (rows where the column is empty stay).
825
827
  - **range**: a number or date column in ranges, as radio buttons ("Any", then each range). A
@@ -1127,7 +1129,7 @@ Attributes (the region's **Settings** in the page designer):
1127
1129
  | Key | Meaning |
1128
1130
  |---|---|
1129
1131
  | `list` | The list's name (upper case) |
1130
- | `template` | `links` (default: nested links), `badges` (a row of tiles with the badge as the value), `cards` (a card per entry with its description), `tabs` (the top-level entries as tabs) or `wizard_progress` (numbered steps: the entry of the current page is the current step, the ones before it are done and link back; APEX: Wizard Progress) |
1132
+ | `template` | `links` (default: nested links), `badges` (a row of tiles with the badge as the value), `cards` (a card per entry with its description; (sprint 88) a colour band and the icon in a round badge, in the chart palette's order), `tabs` (the top-level entries as tabs) or `wizard_progress` (numbered steps: the entry of the current page is the current step, the ones before it are done and link back; APEX: Wizard Progress) |
1131
1133
 
1132
1134
  The HR example's page 31 (Shortcuts) shows a static list with each template (with a badge from an
1133
1135
  item, child entries, a manager-only entry and one of an excluded build option) and a department
@@ -1198,6 +1200,76 @@ region when an application is replaced. The HR example's page 36 (*My reports*)
1198
1200
  (a view without the user name and photo columns) and leave requests (row level security: an
1199
1201
  employee sees only their own), with King's shared report *Salary by department*.
1200
1202
 
1203
+ ### `form_designer` (Form Designer)
1204
+
1205
+ A **Form Designer** region (sprint 85; pgkiln's own, APEX has no counterpart) lets the application's
1206
+ administrators build their own data entry forms in the running application, without a developer
1207
+ and without a new release. Like the [Data Reporter](#data_reporter-data-reporter), the developer
1208
+ decides *what* may be edited; the administrators decide *how* the forms look and who uses them.
1209
+
1210
+ **The developer** places the region (page designer gallery: *Form Designer*) and, in its
1211
+ **Settings**, chooses **who may design forms** (an [authorization scheme](08-security.md); without
1212
+ one nobody can) and adds **data sources**: a table or view each (pgkiln's own `meta` schema and the
1213
+ system schemas are never offered), with a static id, a label and a description. Per source:
1214
+
1215
+ - the **columns** forms may show and change, with a label and, for a select list or radio group,
1216
+ a **list of values**: a shared one of the application (`LOV:NAME`) or `STATIC:Display;value,…`
1217
+ (never SQL: designers can't write queries);
1218
+ - the **key column** that identifies a row (the primary key of a new table; choose one for a view),
1219
+ needed to change and delete rows;
1220
+ - what forms on it **may** do at most: **add**, **change** and **delete** rows.
1221
+
1222
+ A new source offers all its columns except binary and JSON ones, allows adding and (with a key)
1223
+ changing rows, never deleting. Untick generated keys and audit columns; the builder warns when the
1224
+ application's database role lacks a privilege a source needs.
1225
+
1226
+ **Designers** (users that pass the scheme) see *New form* and a *Design form* link under each
1227
+ form. The designer is a plain form (it works without JavaScript): a name, a description, what
1228
+ users may do (within what the source allows) and a row per offered column: *On the form*,
1229
+ *Order*, *Label*, *Help text*, *Required*, *Display* (a field by the column's type; a text area for
1230
+ text; a select list or radio group for a column with a list of values) and a *Default* for new
1231
+ rows, and *In the list* for the columns of the form's list of rows. A form is a **draft** until
1232
+ *Published* is ticked: only designers see drafts.
1233
+
1234
+ **Users** see the published forms as cards. A form with list columns opens on its rows (newest
1235
+ first, paged, `page_size`) with *Add* and an *Edit* link per row; a form without list columns
1236
+ only adds rows: it opens on an empty form, and after *Save* on the next empty one. Fields follow
1237
+ the column's type: a number field, the [date picker](05-items.md#the-date-picker) in the
1238
+ application's date format for dates, the browser's date and time field for timestamps, a
1239
+ checkbox for booleans.
1240
+
1241
+ How it stays safe:
1242
+
1243
+ - rows are read and written as the **application's database role**: grants and [row level
1244
+ security](08-security.md) apply (in the HR example an employee changes only their own meetings);
1245
+ - a form's definition is checked against the region's settings before it renders or saves: only
1246
+ offered columns that exist and can be read, displays the column's type allows, actions the
1247
+ source allows; anything else is dropped. Only the form's fields are written; identifiers are
1248
+ only the developer's schema, table and column names, always quoted; values and row keys are
1249
+ always parameters; a value of a list of values must be in the list;
1250
+ - **lost updates** are refused: a row changed since its form was opened is not overwritten (the
1251
+ user sees what they typed, with a message);
1252
+ - forms are stored in `meta.user_form`, reached by applications only through the view
1253
+ `meta.user_forms` (the forms of the application) and the functions `meta.save_user_form` /
1254
+ `meta.delete_user_form`; saving rows and forms needs the CSRF token and a region the user can
1255
+ see, designing also the region's authorization scheme. Every saved, changed or deleted row and
1256
+ form is in the activity log (event `form_designer`).
1257
+
1258
+ Attributes (written by the region's **Settings**):
1259
+
1260
+ | Key | Default | Meaning |
1261
+ |---|---|---|
1262
+ | `sources` | | `[{"id": "orders", "label": "Orders", "description": "…", "schema": "sales", "table": "orders", "key": "id", "columns": [{"name": "customer", "label": "Customer"}, {"name": "status", "lov": "LOV:ORDER_STATUS"}], "insert": true, "update": true, "delete": false}]` |
1263
+ | `design_authz` | | The authorization scheme designers pass; none: nobody designs |
1264
+ | `page_size` | `25` | Rows per page of a form's list (5 to 200) |
1265
+ | `empty` | `No data found.` | Text when a form's list has no rows |
1266
+
1267
+ The sources travel with the application export (they are the region's attributes); the forms are
1268
+ data of the installation, like Data Reporter reports: not exported, and they stay with their
1269
+ region when an application is replaced. The HR example's page 45 (*Forms*, under Administration)
1270
+ offers departments, meetings and employees (changing only) to administrators, with the published
1271
+ forms *Departments* and *Book a meeting*.
1272
+
1201
1273
  ### `ai_assistant` (AI assistant)
1202
1274
 
1203
1275
  An **AI assistant** region (APEX: AI Assistant, AI agents and tools) is a chat with an
@@ -11,7 +11,7 @@ page, optionally inside a region. Name them `P<page>_<NAME>` (uppercase letters,
11
11
  | `text` | text field | text |
12
12
  | `textarea` | multi-line text (always full width) | text |
13
13
  | `number` | number field (numeric keyboard on phones) | e.g. `1250.50` |
14
- | `date` | date picker: the browser's, or (sprint 76) with an application date format or a date format mask on the item a text field in that format with a calendar button; typed dates are read back in the format | `YYYY-MM-DD` |
14
+ | `date` | a text field in the item's date format mask, else the application's date format, else `YYYY-MM-DD`, with pgkiln's [date picker](#the-date-picker) (sprint 84; was the browser's); typed dates are read back in the format | `YYYY-MM-DD` |
15
15
  | `datetime` | date-time picker | `YYYY-MM-DDTHH:MM` |
16
16
  | `email`, `tel`, `url` | typed fields with the matching phone keyboard | text |
17
17
  | `password` | password field; never shown back, empty = unchanged | text |
@@ -31,7 +31,7 @@ page, optionally inside a region. Name them `P<page>_<NAME>` (uppercase letters,
31
31
  | `markdown` | Markdown textarea with a toolbar; shown formatted when read-only | the Markdown text |
32
32
  | `rating` | star rating (radio buttons drawn as stars) | `1` … `max` (default 5) |
33
33
  | `combobox` | free text with suggestions from the list of values; several values become tags | **colon-separated** values, e.g. `SQL:Sales` |
34
- | `daterange` | two date pickers, *From* and *To* | `from:to`, e.g. `2026-01-01:2026-06-30` |
34
+ | `daterange` | two date fields, *From* and *To*, in the date format with the date picker (the picker keeps *From* on or before *To*) | `from:to`, e.g. `2026-01-01:2026-06-30` |
35
35
  | `qrcode` | display only: the value as a QR code (SVG drawn on the server) | whatever was set |
36
36
  | `image` | display only: a picture from a URL in the value, or a BLOB from a query ([Display image](#display-image-and-geocoded-address)) | a URL, or nothing |
37
37
  | `address` | a text field with *Check address*: the installation's geocoder offers matching addresses ([Geocoded address](#display-image-and-geocoded-address)) | the address as text |
@@ -51,6 +51,18 @@ where hiredate between nullif(split_part(:P20_PERIOD, ':', 1), '')::date
51
51
  and coalesce(nullif(split_part(:P20_PERIOD, ':', 2), '')::date, 'infinity')
52
52
  ```
53
53
 
54
+
55
+ ### The date picker
56
+
57
+ (sprint 84; APEX: the date picker) A date item, a date range item, an interactive grid's date cell and a
58
+ faceted search's own date range and the task list's vacation rule are text in the date format: the item's date format mask, else the
59
+ application's date format (Settings → Globalization), else `YYYY-MM-DD`. The calendar button next to the
60
+ field (or Alt+↓ in it) opens a month of days under it: the chosen day marked, today outlined, the week
61
+ starting on the language's first day. Arrow keys move a day or a week, Page Up and Page Down a month
62
+ (with Shift a year), Home and End to the week's first and last day, Enter or Space choose, Escape
63
+ closes; *Today* and *Clear* below. Typed dates are read back in the format (and `YYYY-MM-DD` always
64
+ works), so the field works without JavaScript. Timestamp items and grid timestamp cells still use the
65
+ browser's date and time field.
54
66
  ## Properties
55
67
 
56
68
  | Property | Meaning |
@@ -427,7 +427,7 @@ navigation entries and application processes have the same `build_option` column
427
427
  | Column | Description |
428
428
  |---|---|
429
429
  | `page_id`, `seq`, `title` | |
430
- | `type` | `report`, `grid`, `form`, `chart`, `cards`, `calendar`, `facets`, `smart_filters`, `display_selector`, `map`, `tree`, `template_component`, `list`, `data_reporter`, `ai_assistant`, `tasks`, `workflows`, `static`, `dynamic` |
430
+ | `type` | `report`, `grid`, `form`, `chart`, `cards`, `calendar`, `facets`, `smart_filters`, `display_selector`, `map`, `tree`, `template_component`, `list`, `data_reporter`, `form_designer`, `ai_assistant`, `tasks`, `workflows`, `static`, `dynamic` |
431
431
  | `source` | SELECT (or HTML for `static`) |
432
432
  | `table_name`, `pk_column` | For `form` and `grid` |
433
433
  | `pk_item` | For `form`: the item holding the key |
@@ -169,7 +169,8 @@ Tips:
169
169
  together.
170
170
  - **Errors are hidden by default**: unexpected errors show a reference number unless the app is in
171
171
  debug mode.
172
- - **Dates**: date items use ISO format (`2026-10-01`) and the browser's date picker; format masks
172
+ - **Dates**: date items, date ranges, grid date cells and facet ranges are text in the application's date format
173
+ (else ISO, `2026-10-01`) with pgkiln's date picker; format masks
173
174
  apply to report, grid and cards columns and to number and display items
174
175
  ([number formats](14-globalization.md#number-formats)).
175
176
  - **Modal pages** open over the calling page, and close and refresh it after a successful submit,
@@ -154,6 +154,8 @@ src/
154
154
  tasks.ts task list region and task actions (approvals); (094) the vacation rule panel and route
155
155
  data-reporter.ts Data Reporter region (migration 057): sources from the region's config, checkDef (offered columns, whitelists),
156
156
  reportQuery/chartQuery, the list and editor (GET form), save/delete routes (meta.save_data_report)
157
+ form-designer.ts Form Designer region (migration 106): sources from the region's config, checkFormDef (offered columns, displays,
158
+ actions), forms as cards, a form's rows, a row's form (lost update hash), the designer; row/design/delete routes
157
159
  workflows.ts workflow console region and its actions
158
160
  ai.ts Generate text with AI: the ai_generate process (config checks, &ITEM. as delimited escaped data,
159
161
  schemas from items, answers into items), aiInputs/aiOutputs for its dynamic action (route in routes.ts)
@@ -209,7 +211,9 @@ src/
209
211
  mail.ts Workspace utilities → Mail services (administrators: providers, write-only secret, allowed senders,
210
212
  applications with From and daily limit, test) and Applications → Activity → Mail (retry, cancel)
211
213
  shared.ts Shared Components and access control
212
- designer.ts page designer: component tree (with computations and branches), layout canvas and gallery, property editor, toolbar
214
+ designer.ts page designer: component tree (with computations and branches), layout canvas and gallery, property editor, toolbar;
215
+ (sprint 86) GET /builder/apps/:app/pages/:no, /builder/pages/:id redirects browsers there (designerUrl in ui.ts)
216
+ errors.ts (sprint 86) the builder's HTML 404 and 500 pages for browsers (wantsHtml, a not-found handler and an onSend hook)
213
217
  arrange.ts page designer layout changes: move, column span, create from the gallery, undo / redo
214
218
  sql.ts SQL Workshop: the home and SQL commands (templates and drafts to review)
215
219
  object-browser.ts (sprint 75) the Object Browser: objects by type, system objects hidden, a table in tabs, column actions as SQL to review
@@ -241,6 +245,7 @@ src/
241
245
  themeroller.ts Settings → Theme Roller: style variants (add, edit, rename, delete), default style, users may choose
242
246
  subscriptions.ts Shared Components → Subscriptions: subscribe, refresh, unsubscribe, subscribers and publish; the note under a component
243
247
  reporter.ts page designer: Data Reporter settings (sources: table or view, offered columns, labels, masks; sharing)
248
+ form-designer.ts page designer: Form Designer settings (designers' scheme; sources: key, actions, offered columns, labels, lists of values)
244
249
  workingcopies.ts Working copies: list and create, compare with differences, merge or refresh with conflict choices, delete
245
250
  rest.ts REST module endpoints list and curl example (Shared Components)
246
251
  workflows.ts workflow versions, diagram and instances (Shared Components)
@@ -0,0 +1,80 @@
1
+ -- =====================================================================
2
+ -- HR example, part 61: a Form Designer (docs/guide/04-pages-and-regions.md,
3
+ -- "Form Designer")
4
+ --
5
+ -- Page 45 "Forms": the application's administrators (authorization scheme
6
+ -- ADMIN) build data entry forms on three tables the developer offers in the
7
+ -- region's settings, and publish them for every employee:
8
+ -- - "Departments": hr.dept, add and change rows;
9
+ -- - "Meetings": hr.meeting, add, change and delete rows; its row level
10
+ -- security still applies (an employee changes only their own meetings);
11
+ -- - "Employees": hr.emp, change rows only, with the job and department
12
+ -- as lists of values.
13
+ -- Two published forms are there to start from: "Departments" (a list of
14
+ -- departments with Add and Edit) and "Book a meeting" (no list: the form
15
+ -- only adds rows). Forms are installation data: not part of the export.
16
+ -- =====================================================================
17
+
18
+ insert into meta.page (app_id, page_no, name, title, parent_page)
19
+ select id, 45, 'Forms', 'Forms', 1 from meta.app where alias = 'hr';
20
+
21
+ insert into meta.region (page_id, seq, title, type, columns, template, config)
22
+ select p.id, 10, 'Form Designer', 'form_designer', 12, 'standard', $j${
23
+ "design_authz": "ADMIN",
24
+ "page_size": 15,
25
+ "sources": [
26
+ {"id": "departments", "label": "Departments", "description": "Department names and locations.",
27
+ "schema": "hr", "table": "dept", "key": "deptno", "insert": true, "update": true, "delete": false,
28
+ "columns": [{"name": "dname", "label": "Department"}, {"name": "loc", "label": "Location"}]},
29
+ {"id": "meetings", "label": "Meetings", "description": "Meetings in the planner; employees change only their own.",
30
+ "schema": "hr", "table": "meeting", "key": "id", "insert": true, "update": true, "delete": true,
31
+ "columns": [{"name": "title", "label": "Title"}, {"name": "starts_at", "label": "Starts"}, {"name": "ends_at", "label": "Ends"}, {"name": "private", "label": "Private"}]},
32
+ {"id": "employees", "label": "Employees", "description": "Job, department and salary of employees.",
33
+ "schema": "hr", "table": "emp", "key": "empno", "insert": false, "update": true, "delete": false,
34
+ "columns": [{"name": "ename", "label": "Name"}, {"name": "job", "label": "Job", "lov": "LOV:JOBS"}, {"name": "deptno", "label": "Department", "lov": "LOV:DEPARTMENTS"},
35
+ {"name": "hiredate", "label": "Hire date"}, {"name": "sal", "label": "Salary"}]}
36
+ ]}$j$::jsonb
37
+ from meta.page p join meta.app a on a.id = p.app_id
38
+ where a.alias = 'hr' and p.page_no = 45;
39
+
40
+ -- two published forms to start from (forms are installation data: not part of the export)
41
+ insert into meta.user_form (app_id, region_id, name, description, published, definition, created_by, updated_by)
42
+ select a.id, r.id, f.name, f.description, true, f.definition, 'king', 'king'
43
+ from meta.app a join meta.page p on p.app_id = a.id and p.page_no = 45 join meta.region r on r.page_id = p.id and r.type = 'form_designer',
44
+ (values
45
+ ('Departments', 'Add a department or change its name and location.',
46
+ '{"source": "departments",
47
+ "fields": [{"column": "dname", "label": "", "help": "", "required": true, "display": "auto", "default": ""},
48
+ {"column": "loc", "label": "", "help": "The city.", "required": false, "display": "auto", "default": ""}],
49
+ "list": ["dname", "loc"], "actions": {"insert": true, "update": true, "delete": false}}'::jsonb),
50
+ ('Book a meeting', 'Put a meeting in the planner.',
51
+ '{"source": "meetings",
52
+ "fields": [{"column": "title", "label": "What", "help": "", "required": true, "display": "auto", "default": ""},
53
+ {"column": "starts_at", "label": "", "help": "", "required": true, "display": "auto", "default": ""},
54
+ {"column": "ends_at", "label": "", "help": "Leave empty for a meeting without an end.", "required": false, "display": "auto", "default": ""},
55
+ {"column": "private", "label": "Only visible to me", "help": "", "required": false, "display": "auto", "default": "false"}],
56
+ "list": [], "actions": {"insert": true, "update": false, "delete": false}}'::jsonb)
57
+ ) as f (name, description, definition)
58
+ where a.alias = 'hr';
59
+
60
+ insert into meta.nav_entry (app_id, parent_id, seq, label, icon, target_page)
61
+ select a.id, n.id, 60, 'Forms', 'file', 45
62
+ from meta.app a join meta.nav_entry n on n.app_id = a.id and n.label = 'Administration' and n.parent_id is null
63
+ where a.alias = 'hr';
64
+
65
+ insert into meta.translation (app_id, language, source, target)
66
+ select a.id, 'nl', t.source, t.target
67
+ from meta.app a, (values
68
+ ('Forms', 'Formulieren'),
69
+ ('Form Designer', 'Formulierbouwer'),
70
+ ('Department names and locations.', 'Namen en vestigingsplaatsen van afdelingen.'),
71
+ ('Meetings', 'Vergaderingen'),
72
+ ('Meetings in the planner; employees change only their own.', 'Vergaderingen in de planner; medewerkers wijzigen alleen hun eigen.'),
73
+ ('Job, department and salary of employees.', 'Functie, afdeling en salaris van medewerkers.'),
74
+ ('Title', 'Titel'),
75
+ ('Starts', 'Begint'),
76
+ ('Ends', 'Eindigt'),
77
+ ('Private', 'Privé')
78
+ ) as t (source, target)
79
+ where a.alias = 'hr'
80
+ on conflict do nothing;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "pgkiln",
3
- "version": "0.45.0",
3
+ "version": "0.46.0",
4
4
  "description": "Open source low-code, SQL-driven application builder for PostgreSQL, inspired by Oracle APEX",
5
5
  "keywords": [
6
6
  "postgresql",