pgkiln 0.53.0 → 0.55.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.
- package/SECURITY.md +2 -0
- package/db/migrations/127_builder_language.sql +16 -0
- package/docs/apex-feature-parity.md +7 -7
- package/docs/guide/03-builder.md +62 -8
- package/docs/guide/12-development.md +50 -3
- package/examples/gallery/README.md +37 -6
- package/examples/gallery/gallery.json +23 -1
- package/examples/gallery/samples/sample-calendar.sql +172 -0
- package/examples/gallery/samples/sample-cards.sql +157 -0
- package/examples/gallery/samples/sample-charts.sql +265 -0
- package/examples/gallery/samples/sample-dynamic-actions.sql +177 -0
- package/examples/gallery/samples/sample-grids.sql +151 -0
- package/examples/gallery/samples/sample-reporting.sql +151 -0
- package/examples/gallery/samples/ux-patterns.sql +295 -0
- package/examples/gallery/utilities/announcements.json +24 -0
- package/examples/gallery/utilities/lookup-lists.json +28 -0
- package/examples/gallery/utilities/release-notes.json +41 -0
- package/package.json +1 -1
- package/public/app.css +2 -0
- package/public/builder.css +39 -1
- package/public/worksheet.css +3 -2
- package/src/builder/advisor.ts +1 -1
- package/src/builder/ai-agents.ts +5 -5
- package/src/builder/ai-builder.ts +3 -3
- package/src/builder/api.ts +1 -1
- package/src/builder/autorest.ts +2 -2
- package/src/builder/availability.ts +1 -1
- package/src/builder/backups.ts +1 -1
- package/src/builder/changelog.ts +1 -1
- package/src/builder/data-modeler.ts +4 -4
- package/src/builder/data-security.ts +1 -1
- package/src/builder/dataload.ts +1 -1
- package/src/builder/datapump.ts +1 -1
- package/src/builder/ddl-tools.ts +2 -2
- package/src/builder/designer.ts +134 -124
- package/src/builder/diagnostics.ts +2 -2
- package/src/builder/errors.ts +10 -12
- package/src/builder/feedback.ts +1 -1
- package/src/builder/gallery.ts +94 -47
- package/src/builder/globalization.ts +1 -1
- package/src/builder/home.ts +59 -57
- package/src/builder/i18n/de.ts +675 -0
- package/src/builder/i18n/en.ts +678 -0
- package/src/builder/i18n/es.ts +675 -0
- package/src/builder/i18n/fr.ts +675 -0
- package/src/builder/i18n/nl.ts +675 -0
- package/src/builder/i18n/pt.ts +675 -0
- package/src/builder/i18n.ts +116 -0
- package/src/builder/language.ts +74 -0
- package/src/builder/launchpad.ts +13 -8
- package/src/builder/object-browser.ts +1 -1
- package/src/builder/plugins.ts +1 -1
- package/src/builder/querybuilder.ts +1 -1
- package/src/builder/quicksql.ts +1 -1
- package/src/builder/rest-modules.ts +2 -2
- package/src/builder/routes.ts +88 -66
- package/src/builder/sampledata.ts +1 -1
- package/src/builder/scheduling.ts +1 -1
- package/src/builder/scripts.ts +1 -1
- package/src/builder/search.ts +1 -1
- package/src/builder/shared.ts +5 -5
- package/src/builder/sql-charts.ts +2 -2
- package/src/builder/sql-monitor.ts +2 -2
- package/src/builder/sql-tools.ts +6 -0
- package/src/builder/sql-utilities.ts +3 -3
- package/src/builder/sql.ts +24 -22
- package/src/builder/static-files.ts +1 -1
- package/src/builder/subscriptions.ts +1 -1
- package/src/builder/supporting.ts +1 -1
- package/src/builder/table-editor.ts +1 -1
- package/src/builder/team.ts +7 -5
- package/src/builder/themeroller.ts +1 -1
- package/src/builder/top-sql.ts +1 -1
- package/src/builder/ui.ts +87 -77
- package/src/builder/unload.ts +1 -1
- package/src/builder/wizards.ts +1 -1
- package/src/builder/workflow-migrate.ts +1 -1
- package/src/builder/workingcopies.ts +2 -2
- package/src/builder/worksheet.ts +1 -1
- package/src/builder/workspace-home.ts +62 -59
- package/src/builder/workspaces.ts +2 -1
- package/src/runtime/charts.ts +4 -1
- package/src/runtime/regions.ts +8 -5
|
@@ -0,0 +1,177 @@
|
|
|
1
|
+
-- =====================================================================
|
|
2
|
+
-- Sample app "Sample Dynamic Actions" (App Gallery → Sample Apps)
|
|
3
|
+
--
|
|
4
|
+
-- Behaviour in the browser without JavaScript of your own:
|
|
5
|
+
-- page 2 show, hide, enable and disable items on a condition;
|
|
6
|
+
-- page 3 values from the server (set value, execute SQL) and a report
|
|
7
|
+
-- refreshed when an item changes;
|
|
8
|
+
-- page 4 messages, confirmations, a wait popup, classes, item errors,
|
|
9
|
+
-- an inline dialog and actions on page load and key release.
|
|
10
|
+
--
|
|
11
|
+
-- Installed by an administrator from the App Gallery, or with
|
|
12
|
+
-- psql "$DATABASE_URL" -1 -f examples/gallery/samples/sample-dynamic-actions.sql
|
|
13
|
+
-- then open /a/sample-dynamic-actions and sign in as demo / demo.
|
|
14
|
+
-- =====================================================================
|
|
15
|
+
|
|
16
|
+
create or replace function pg_temp.page_id(p_alias text, p_page int) returns int language sql stable as
|
|
17
|
+
$$ select p.id from meta.page p join meta.app a on a.id = p.app_id where a.alias = p_alias and p.page_no = p_page $$;
|
|
18
|
+
create or replace function pg_temp.region_id(p_alias text, p_page int, p_static text) returns int language sql stable as
|
|
19
|
+
$$ select r.id from meta.region r where r.page_id = pg_temp.page_id(p_alias, p_page) and r.static_id = p_static $$;
|
|
20
|
+
|
|
21
|
+
-- 1. Schema and data ---------------------------------------------------------
|
|
22
|
+
create schema sample_da;
|
|
23
|
+
|
|
24
|
+
create table sample_da.product (
|
|
25
|
+
id int generated always as identity primary key,
|
|
26
|
+
name text not null unique,
|
|
27
|
+
category text not null,
|
|
28
|
+
price numeric(10, 2) not null
|
|
29
|
+
);
|
|
30
|
+
|
|
31
|
+
insert into sample_da.product (name, category, price) values
|
|
32
|
+
('Notebook', 'Paper', 3.50), ('Sticky notes', 'Paper', 2.25), ('Printer paper', 'Paper', 6.95),
|
|
33
|
+
('Ballpoint', 'Pens', 1.20), ('Fountain pen', 'Pens', 24.00), ('Marker set', 'Pens', 8.40),
|
|
34
|
+
('Stapler', 'Desk', 12.50), ('Desk organiser', 'Desk', 18.00), ('Scissors', 'Desk', 6.75);
|
|
35
|
+
|
|
36
|
+
-- 2. The application's database role ----------------------------------------
|
|
37
|
+
do $$ begin
|
|
38
|
+
if not exists (select 1 from pg_roles where rolname = 'app_sample_da') then create role app_sample_da nologin; end if;
|
|
39
|
+
end $$;
|
|
40
|
+
grant app_sample_da to pgkiln_runtime;
|
|
41
|
+
grant usage on schema sample_da to app_sample_da;
|
|
42
|
+
grant select on all tables in schema sample_da to app_sample_da;
|
|
43
|
+
|
|
44
|
+
-- 3. The application ----------------------------------------------------------
|
|
45
|
+
insert into meta.app (alias, name, home_page, authentication, db_role)
|
|
46
|
+
values ('sample-dynamic-actions', 'Sample Dynamic Actions', 1, 'app_users', 'app_sample_da');
|
|
47
|
+
|
|
48
|
+
insert into meta.app_user (app_id, username, password_hash, roles)
|
|
49
|
+
select id, 'demo', meta.hash_password('demo'), '{}' from meta.app where alias = 'sample-dynamic-actions';
|
|
50
|
+
|
|
51
|
+
insert into meta.page (app_id, page_no, name, title, parent_page)
|
|
52
|
+
select a.id, p.no, p.name, p.name, p.parent
|
|
53
|
+
from meta.app a, (values
|
|
54
|
+
(1, 'Sample Dynamic Actions', null), (2, 'Show, hide, enable', 1), (3, 'Values from the server', 1), (4, 'Messages and effects', 1)
|
|
55
|
+
) as p (no, name, parent)
|
|
56
|
+
where a.alias = 'sample-dynamic-actions';
|
|
57
|
+
|
|
58
|
+
insert into meta.nav_entry (app_id, seq, label, icon, target_page)
|
|
59
|
+
select a.id, n.seq, n.label, n.icon, n.page
|
|
60
|
+
from meta.app a, (values
|
|
61
|
+
(10, 'Home', 'home', 1), (20, 'Show, hide, enable', 'eye', 2), (30, 'Values from the server', 'refresh', 3), (40, 'Messages and effects', 'bell', 4)
|
|
62
|
+
) as n (seq, label, icon, page)
|
|
63
|
+
where a.alias = 'sample-dynamic-actions';
|
|
64
|
+
|
|
65
|
+
insert into meta.list (app_id, name, type, description)
|
|
66
|
+
select id, 'DA_TOPICS', 'static', 'The pages of Sample Dynamic Actions' from meta.app where alias = 'sample-dynamic-actions';
|
|
67
|
+
|
|
68
|
+
insert into meta.list_entry (app_id, list_name, seq, label, icon, target_page, description)
|
|
69
|
+
select a.id, 'DA_TOPICS', e.seq, e.label, e.icon, e.page, e.description
|
|
70
|
+
from meta.app a, (values
|
|
71
|
+
(10, 'Show, hide, enable', 'eye', 2, 'Items that appear, disappear or switch on depending on another item.'),
|
|
72
|
+
(20, 'Values from the server', 'refresh', 3, 'SQL fills in items and refreshes a report, without reloading the page.'),
|
|
73
|
+
(30, 'Messages and effects', 'bell', 4, 'Alerts, confirmations, a wait popup, classes, item errors and an inline dialog.')
|
|
74
|
+
) as e (seq, label, icon, page, description)
|
|
75
|
+
where a.alias = 'sample-dynamic-actions';
|
|
76
|
+
|
|
77
|
+
insert into meta.region (page_id, seq, title, type, columns, template, source, config, static_id)
|
|
78
|
+
select pg_temp.page_id('sample-dynamic-actions', r.page), r.seq, r.title, r.type, r.cols, r.template, r.source, r.config::jsonb, r.sid
|
|
79
|
+
from (values
|
|
80
|
+
-- 1 Home
|
|
81
|
+
(1, 10, 'Sample Dynamic Actions', 'static', 12, 'hero',
|
|
82
|
+
'<p>A dynamic action is an <strong>event</strong> (an item changes, a button is clicked, the page loads), an optional <strong>condition</strong> on the item''s value and an <strong>action</strong>: show or hide, set a value with SQL, refresh a region, show a message. The actions of one event run in sequence. They need JavaScript; without it the pages still work, without these conveniences.</p>',
|
|
83
|
+
'{"icon": "bolt"}', null),
|
|
84
|
+
(1, 20, 'Topics', 'list', 12, 'plain', null, '{"list": "DA_TOPICS", "template": "cards"}', null),
|
|
85
|
+
|
|
86
|
+
-- 2 Show, hide, enable
|
|
87
|
+
(2, 5, 'About this page', 'static', 12, 'plain',
|
|
88
|
+
'<p class="lead"><strong>Show</strong> with a condition shows the company fields when <em>Customer type</em> equals Company, and hides them otherwise: one dynamic action does both, also when the page loads (already on the server, so nothing flickers). <strong>Enable</strong> works the same way for the e-mail address.</p>',
|
|
89
|
+
'{}', null),
|
|
90
|
+
(2, 10, 'Customer', 'static', 6, 'standard', null, '{}', 'customer'),
|
|
91
|
+
(2, 20, 'Newsletter', 'static', 6, 'standard', null, '{}', 'newsletter'),
|
|
92
|
+
|
|
93
|
+
-- 3 Values from the server
|
|
94
|
+
(3, 5, 'About this page', 'static', 12, 'plain',
|
|
95
|
+
'<p class="lead"><strong>Set value</strong> runs a SELECT on the server with the items it submits and puts the columns of its first row into the affected items: choosing a product fills in its price, changing the quantity the total. <strong>Refresh region</strong> draws the report again with the chosen category. The SQL never reaches the browser.</p>',
|
|
96
|
+
'{}', null),
|
|
97
|
+
(3, 10, 'Order line', 'static', 6, 'standard', null, '{}', 'order-line'),
|
|
98
|
+
(3, 20, 'Filter', 'static', 6, 'standard', null, '{}', 'filter'),
|
|
99
|
+
(3, 30, 'Products', 'report', 12, 'standard',
|
|
100
|
+
E'select name, category, price from sample_da.product\n where :P3_CATEGORY is null or category = :P3_CATEGORY\n order by category, name',
|
|
101
|
+
'{"formats": {"price": "999G990D00"}}', 'products'),
|
|
102
|
+
|
|
103
|
+
-- 4 Messages and effects
|
|
104
|
+
(4, 5, 'About this page', 'static', 12, 'plain',
|
|
105
|
+
'<p class="lead">Each button has the action <em>Defined by dynamic action</em>. <strong>Confirm</strong> stops the actions after it when you answer No; <strong>Wait popup</strong> covers the page while an Ajax call runs; <strong>Add class</strong> and <strong>Remove class</strong> change a region''s look; <strong>Show error</strong> puts a message under an item; <strong>Open region</strong> opens an inline dialog. Typing in the name runs a <em>Key release</em> action, and the page load puts the cursor in it.</p>',
|
|
106
|
+
'{}', null),
|
|
107
|
+
(4, 10, 'Try the actions', 'static', 12, 'buttons_container', null, '{}', 'buttons'),
|
|
108
|
+
(4, 20, 'Greeting', 'static', 6, 'standard', null, '{}', 'greeting'),
|
|
109
|
+
(4, 30, 'A region to highlight', 'static', 6, 'standard',
|
|
110
|
+
'<p>The buttons <em>Highlight</em> and <em>Mute</em> add the theme''s classes <code>is-highlight</code> and <code>is-muted</code> to this region; <em>Reset</em> removes them.</p>',
|
|
111
|
+
'{}', 'target'),
|
|
112
|
+
(4, 40, 'An inline dialog', 'static', 12, 'inline_dialog',
|
|
113
|
+
'<p>This region has the template <em>Inline dialog</em>: hidden until a dynamic action (or a link to its id) opens it. Escape, the close button or a click beside it closes it.</p>',
|
|
114
|
+
'{}', 'dialog')
|
|
115
|
+
) as r (page, seq, title, type, cols, template, source, config, sid);
|
|
116
|
+
|
|
117
|
+
insert into meta.item (page_id, region_id, seq, name, label, type, lov, default_value, help, config)
|
|
118
|
+
select pg_temp.page_id('sample-dynamic-actions', i.page), pg_temp.region_id('sample-dynamic-actions', i.page, i.region),
|
|
119
|
+
i.seq, i.name, i.label, i.type, i.lov, i.def, i.help, i.config::jsonb
|
|
120
|
+
from (values
|
|
121
|
+
(2, 'customer', 10, 'P2_TYPE', 'Customer type', 'radio', 'STATIC:Person;PERSON,Company;COMPANY', 'PERSON', null, '{}'),
|
|
122
|
+
(2, 'customer', 20, 'P2_NAME', 'Name', 'text', null, null, null, '{}'),
|
|
123
|
+
(2, 'customer', 30, 'P2_COMPANY', 'Company name', 'text', null, null, 'Shown only for companies.', '{}'),
|
|
124
|
+
(2, 'customer', 40, 'P2_VAT', 'VAT number', 'text', null, null, 'Shown only for companies.', '{}'),
|
|
125
|
+
(2, 'newsletter', 10, 'P2_NEWSLETTER', 'Send me the newsletter', 'radio', 'STATIC:Yes;YES,No;NO', 'NO', null, '{}'),
|
|
126
|
+
(2, 'newsletter', 20, 'P2_EMAIL', 'E-mail address', 'email', null, null, 'Enabled only when you want the newsletter.', '{}'),
|
|
127
|
+
(3, 'order-line', 10, 'P3_PRODUCT', 'Product', 'select', 'select name, id from sample_da.product order by name', null, null, '{"warn_unsaved": false}'),
|
|
128
|
+
(3, 'order-line', 20, 'P3_PRICE', 'Price', 'number', null, null, 'Set by the server when you choose a product.', '{"warn_unsaved": false}'),
|
|
129
|
+
(3, 'order-line', 30, 'P3_QTY', 'Quantity', 'number', null, '1', null, '{"warn_unsaved": false}'),
|
|
130
|
+
(3, 'order-line', 40, 'P3_TOTAL', 'Total', 'number', null, null, 'Price times quantity, computed by the server.', '{"warn_unsaved": false}'),
|
|
131
|
+
(3, 'filter', 10, 'P3_CATEGORY', 'Category', 'select', 'select distinct category as d, category as r from sample_da.product order by 1', null,
|
|
132
|
+
'The report below is refreshed when this changes.', '{"warn_unsaved": false, "null_label": "All categories"}'),
|
|
133
|
+
(4, 'greeting', 10, 'P4_NAME', 'Your name', 'text', null, null, 'Type: the greeting follows each key you release.', '{"warn_unsaved": false}'),
|
|
134
|
+
(4, 'greeting', 20, 'P4_GREETING', 'Greeting', 'text', null, null, null, '{"warn_unsaved": false}')
|
|
135
|
+
) as i (page, region, seq, name, label, type, lov, def, help, config);
|
|
136
|
+
|
|
137
|
+
insert into meta.button (page_id, region_id, seq, name, label, action, hot)
|
|
138
|
+
select pg_temp.page_id('sample-dynamic-actions', 4), pg_temp.region_id('sample-dynamic-actions', 4, 'buttons'), b.seq, b.name, b.label, 'da', b.hot
|
|
139
|
+
from (values
|
|
140
|
+
(10, 'ALERT', 'Alert', false), (20, 'CONFIRM', 'Confirm, then a message', true), (30, 'WAIT', 'Wait popup', false),
|
|
141
|
+
(40, 'HIGHLIGHT', 'Highlight', false), (50, 'MUTE', 'Mute', false), (60, 'RESET', 'Reset', false),
|
|
142
|
+
(70, 'ERROR', 'Show an item error', false), (80, 'CLEAR', 'Clear the errors', false), (90, 'DIALOG', 'Open the inline dialog', false)
|
|
143
|
+
) as b (seq, name, label, hot);
|
|
144
|
+
|
|
145
|
+
insert into meta.dynamic_action (page_id, seq, name, event, trigger_element, condition_type, condition_value,
|
|
146
|
+
action, affected_items, affected_region_id, code, items_to_submit, message, css_classes)
|
|
147
|
+
select pg_temp.page_id('sample-dynamic-actions', d.page), d.seq, d.name, d.event, d.trig, d.ctype, d.cvalue,
|
|
148
|
+
d.action, d.items, pg_temp.region_id('sample-dynamic-actions', d.page, d.region), d.code, d.submit, d.message, d.classes
|
|
149
|
+
from (values
|
|
150
|
+
-- page 2
|
|
151
|
+
(2, 10, 'Company fields for companies', 'change', 'P2_TYPE', 'equals', 'COMPANY', 'show', 'P2_COMPANY,P2_VAT', null, null, null, null, null),
|
|
152
|
+
(2, 20, 'E-mail only with the newsletter', 'change', 'P2_NEWSLETTER', 'equals', 'YES', 'enable', 'P2_EMAIL', null, null, null, null, null),
|
|
153
|
+
-- page 3
|
|
154
|
+
(3, 10, 'The product''s price', 'change', 'P3_PRODUCT', null, null, 'set_value', 'P3_PRICE,P3_TOTAL', null,
|
|
155
|
+
'select price, price * coalesce(nullif(:P3_QTY, '''')::numeric, 1) from sample_da.product where id = nullif(:P3_PRODUCT, '''')::int',
|
|
156
|
+
'P3_PRODUCT,P3_QTY', null, null),
|
|
157
|
+
(3, 20, 'The total', 'change', 'P3_QTY', null, null, 'set_value', 'P3_TOTAL', null,
|
|
158
|
+
'select round(coalesce(nullif(:P3_PRICE, '''')::numeric, 0) * coalesce(nullif(:P3_QTY, '''')::numeric, 0), 2)',
|
|
159
|
+
'P3_PRICE,P3_QTY', null, null),
|
|
160
|
+
(3, 30, 'Refresh the products', 'change', 'P3_CATEGORY', null, null, 'refresh_region', null, 'products', null, 'P3_CATEGORY', null, null),
|
|
161
|
+
-- page 4
|
|
162
|
+
(4, 10, 'Put the cursor in the name', 'load', null, null, null, 'set_focus', 'P4_NAME', null, null, null, null, null),
|
|
163
|
+
(4, 20, 'Greet while typing', 'key_release', 'P4_NAME', null, null, 'set_value', 'P4_GREETING', null,
|
|
164
|
+
'select case when coalesce(:P4_NAME, '''') = '''' then '''' else ''Hello, '' || :P4_NAME || ''!'' end', 'P4_NAME', null, null),
|
|
165
|
+
(4, 30, 'An alert', 'click', 'ALERT', null, null, 'alert', null, null, null, null, 'This is an alert: the browser waits until you close it.', null),
|
|
166
|
+
(4, 40, 'Ask first', 'click', 'CONFIRM', null, null, 'confirm', null, null, null, null, 'Show a success message?', null),
|
|
167
|
+
(4, 50, 'Then the message', 'click', 'CONFIRM', null, null, 'show_success', null, null, null, null, 'You said yes: this is a success message.', null),
|
|
168
|
+
(4, 60, 'Wait popup', 'click', 'WAIT', null, null, 'wait_popup', null, null, null, null, 'Working on it…', null),
|
|
169
|
+
(4, 70, 'A slow call', 'click', 'WAIT', null, null, 'execute_sql', null, null, 'select pg_sleep(1.5)', null, null, null),
|
|
170
|
+
(4, 80, 'Done waiting', 'click', 'WAIT', null, null, 'remove_wait_popup', null, null, null, null, null, null),
|
|
171
|
+
(4, 90, 'Highlight the region', 'click', 'HIGHLIGHT', null, null, 'add_class', null, 'target', null, null, null, 'is-highlight'),
|
|
172
|
+
(4, 100, 'Mute the region', 'click', 'MUTE', null, null, 'add_class', null, 'target', null, null, null, 'is-muted'),
|
|
173
|
+
(4, 110, 'Reset the region', 'click', 'RESET', null, null, 'remove_class', null, 'target', null, null, null, 'is-highlight is-muted'),
|
|
174
|
+
(4, 120, 'An item error', 'click', 'ERROR', null, null, 'show_error', 'P4_NAME', null, null, null, 'This is how an error under an item looks.', null),
|
|
175
|
+
(4, 130, 'Clear the errors', 'click', 'CLEAR', null, null, 'clear_errors', null, null, null, null, null, null),
|
|
176
|
+
(4, 140, 'Open the dialog', 'click', 'DIALOG', null, null, 'open_region', null, 'dialog', null, null, null, null)
|
|
177
|
+
) as d (page, seq, name, event, trig, ctype, cvalue, action, items, region, code, submit, message, classes);
|
|
@@ -0,0 +1,151 @@
|
|
|
1
|
+
-- =====================================================================
|
|
2
|
+
-- Sample app "Sample Interactive Grids" (App Gallery → Sample Apps)
|
|
3
|
+
--
|
|
4
|
+
-- The interactive grid: an editable grid made by the wizard; a grid with
|
|
5
|
+
-- select lists, required columns, totals, a frozen column, a row menu and a
|
|
6
|
+
-- default column layout; a master grid with its detail grid; and a
|
|
7
|
+
-- read-only grid with its single row, group by and chart views.
|
|
8
|
+
--
|
|
9
|
+
-- Installed by an administrator from the App Gallery, or with
|
|
10
|
+
-- psql "$DATABASE_URL" -1 -f examples/gallery/samples/sample-grids.sql
|
|
11
|
+
-- then open /a/sample-grids and sign in as demo / demo.
|
|
12
|
+
-- =====================================================================
|
|
13
|
+
|
|
14
|
+
create or replace function pg_temp.page_id(p_alias text, p_page int) returns int language sql stable as
|
|
15
|
+
$$ select p.id from meta.page p join meta.app a on a.id = p.app_id where a.alias = p_alias and p.page_no = p_page $$;
|
|
16
|
+
|
|
17
|
+
-- 1. Schema and data ---------------------------------------------------------
|
|
18
|
+
create schema sample_grids;
|
|
19
|
+
|
|
20
|
+
create table sample_grids.team (
|
|
21
|
+
id int generated always as identity primary key,
|
|
22
|
+
name text not null unique,
|
|
23
|
+
location text,
|
|
24
|
+
budget numeric(12, 2)
|
|
25
|
+
);
|
|
26
|
+
|
|
27
|
+
create table sample_grids.member (
|
|
28
|
+
id int generated always as identity primary key,
|
|
29
|
+
team_id int not null references sample_grids.team,
|
|
30
|
+
name text not null,
|
|
31
|
+
role text not null default 'Developer' check (role in ('Lead', 'Developer', 'Designer', 'Tester', 'Analyst')),
|
|
32
|
+
starts date not null default current_date,
|
|
33
|
+
hours int not null default 40 check (hours between 0 and 60),
|
|
34
|
+
rate numeric(8, 2),
|
|
35
|
+
remote boolean not null default false
|
|
36
|
+
);
|
|
37
|
+
|
|
38
|
+
insert into sample_grids.team (name, location, budget) values
|
|
39
|
+
('Platform', 'Amsterdam', 480000), ('Mobile', 'Rotterdam', 310000), ('Data', 'Utrecht', 260000), ('Design', 'Amsterdam', 150000);
|
|
40
|
+
|
|
41
|
+
insert into sample_grids.member (team_id, name, role, starts, hours, rate, remote) values
|
|
42
|
+
(1, 'Ada', 'Lead', '2019-03-01', 40, 95, false), (1, 'Linus', 'Developer', '2021-06-14', 40, 80, true),
|
|
43
|
+
(1, 'Grace', 'Developer', '2022-01-10', 32, 82, false), (1, 'Ken', 'Tester', '2023-09-01', 24, 65, true),
|
|
44
|
+
(2, 'Margaret', 'Lead', '2020-02-03', 40, 92, false), (2, 'Dennis', 'Developer', '2021-11-22', 36, 78, false),
|
|
45
|
+
(2, 'Barbara', 'Designer', '2024-04-15', 20, 70, true), (3, 'Edgar', 'Analyst', '2018-08-20', 40, 88, false),
|
|
46
|
+
(3, 'Michael', 'Developer', '2022-05-30', 40, 79, true), (3, 'Frances', 'Analyst', '2025-01-06', 28, 72, false),
|
|
47
|
+
(4, 'Susan', 'Designer', '2019-10-01', 40, 85, false), (4, 'Jef', 'Designer', '2023-03-13', 32, 74, true);
|
|
48
|
+
|
|
49
|
+
-- 2. The application's database role ----------------------------------------
|
|
50
|
+
do $$ begin
|
|
51
|
+
if not exists (select 1 from pg_roles where rolname = 'app_sample_grids') then create role app_sample_grids nologin; end if;
|
|
52
|
+
end $$;
|
|
53
|
+
grant app_sample_grids to pgkiln_runtime;
|
|
54
|
+
grant usage on schema sample_grids to app_sample_grids;
|
|
55
|
+
grant select, insert, update, delete on all tables in schema sample_grids to app_sample_grids;
|
|
56
|
+
|
|
57
|
+
-- 3. The application ----------------------------------------------------------
|
|
58
|
+
insert into meta.app (alias, name, home_page, authentication, db_role)
|
|
59
|
+
values ('sample-grids', 'Sample Interactive Grids', 1, 'app_users', 'app_sample_grids');
|
|
60
|
+
|
|
61
|
+
insert into meta.app_user (app_id, username, password_hash, roles)
|
|
62
|
+
select id, 'demo', meta.hash_password('demo'), '{}' from meta.app where alias = 'sample-grids';
|
|
63
|
+
|
|
64
|
+
insert into meta.page (app_id, page_no, name, title, parent_page)
|
|
65
|
+
select a.id, p.no, p.name, p.name, p.parent
|
|
66
|
+
from meta.app a, (values (1, 'Sample Interactive Grids', null), (3, 'Grid features', 1), (5, 'Reading and analysing', 1)) as p (no, name, parent)
|
|
67
|
+
where a.alias = 'sample-grids';
|
|
68
|
+
|
|
69
|
+
-- page 2: an editable grid on the teams, as the Interactive grid wizard makes it
|
|
70
|
+
select meta.generate_page('sample-grids', 'grid', 'sample_grids.team', 2, '{"label": "Teams", "nav": false}');
|
|
71
|
+
-- page 4: master-detail, teams and their members (the Master-detail wizard)
|
|
72
|
+
select meta.generate_page('sample-grids', 'master_detail', 'sample_grids.team', 4, '{"label": "Teams and members", "nav": false}');
|
|
73
|
+
update meta.page set parent_page = 1 where app_id = (select id from meta.app where alias = 'sample-grids') and page_no in (2, 4);
|
|
74
|
+
|
|
75
|
+
insert into meta.nav_entry (app_id, seq, label, icon, target_page)
|
|
76
|
+
select a.id, n.seq, n.label, n.icon, n.page
|
|
77
|
+
from meta.app a, (values
|
|
78
|
+
(10, 'Home', 'home', 1), (20, 'Teams', 'grid', 2), (30, 'Grid features', 'sliders', 3),
|
|
79
|
+
(40, 'Teams and members', 'users', 4), (50, 'Reading and analysing', 'chart', 5)
|
|
80
|
+
) as n (seq, label, icon, page)
|
|
81
|
+
where a.alias = 'sample-grids';
|
|
82
|
+
|
|
83
|
+
insert into meta.list (app_id, name, type, description)
|
|
84
|
+
select id, 'GRID_TOPICS', 'static', 'The pages of Sample Interactive Grids' from meta.app where alias = 'sample-grids';
|
|
85
|
+
|
|
86
|
+
insert into meta.list_entry (app_id, list_name, seq, label, icon, target_page, description)
|
|
87
|
+
select a.id, 'GRID_TOPICS', e.seq, e.label, e.icon, e.page, e.description
|
|
88
|
+
from meta.app a, (values
|
|
89
|
+
(10, 'Teams', 'grid', 2, 'An editable grid as the wizard makes it: edit cells, add rows, delete rows, Save.'),
|
|
90
|
+
(20, 'Grid features', 'sliders', 3, 'Select lists, required columns, totals, a frozen column, a row menu and a column layout.'),
|
|
91
|
+
(30, 'Teams and members', 'users', 4, 'Master-detail: choose a team, edit its members below.'),
|
|
92
|
+
(40, 'Reading and analysing', 'chart', 5, 'A read-only grid with the single row, group by and chart views.')
|
|
93
|
+
) as e (seq, label, icon, page, description)
|
|
94
|
+
where a.alias = 'sample-grids';
|
|
95
|
+
|
|
96
|
+
insert into meta.region (page_id, seq, title, type, columns, template, source, table_name, pk_column, config, static_id)
|
|
97
|
+
select pg_temp.page_id('sample-grids', r.page), r.seq, r.title, r.type, 12, r.template, r.source, r.tab, r.pk, r.config::jsonb, r.sid
|
|
98
|
+
from (values
|
|
99
|
+
-- 1 Home
|
|
100
|
+
(1, 10, 'Sample Interactive Grids', 'static', 'hero',
|
|
101
|
+
'<p>An interactive grid edits the rows of one table in place: click a cell (or press Enter or F2) to edit it, the arrow keys move between cells, Ctrl+C and Ctrl+V copy and paste ranges from and to a spreadsheet. Only changed cells are saved, and when one row fails nothing is saved. Without a <code>grid_dml</code> process a grid is read-only.</p>',
|
|
102
|
+
null, null, '{"icon": "grid"}', null),
|
|
103
|
+
(1, 20, 'Topics', 'list', 'plain', null, null, null, '{"list": "GRID_TOPICS", "template": "cards"}', null),
|
|
104
|
+
|
|
105
|
+
-- 2 Teams (the wizard's grid follows at seq 10)
|
|
106
|
+
(2, 5, 'About this page', 'static', 'plain',
|
|
107
|
+
'<p class="lead">The wizard made this grid: a region of type <code>grid</code> on <code>sample_grids.team</code> with its key column, and a <code>grid_dml</code> process that saves it. Try <strong>Add row</strong>, change a budget and press <strong>Save</strong>; a duplicate name is refused by the table''s unique constraint, with the row number.</p>',
|
|
108
|
+
null, null, '{}', null),
|
|
109
|
+
|
|
110
|
+
-- 3 Grid features
|
|
111
|
+
(3, 5, 'About this page', 'static', 'plain',
|
|
112
|
+
'<p class="lead">The grid''s attributes: <code>columns</code> gives the team column a list of values and makes the name required; <code>aggregates</code> adds totals over every row of the search; <code>frozen</code> keeps the name in view while the grid scrolls sideways; <code>row_actions</code> adds a menu per row (duplicate, delete); <code>layout</code> sets the default order and widths, and every user can rearrange it (Actions → Columns).</p>',
|
|
113
|
+
null, null, '{}', null),
|
|
114
|
+
(3, 10, 'Members', 'grid', 'standard',
|
|
115
|
+
E'select id, name, team_id, role, starts, hours, rate, remote\n from sample_grids.member order by team_id, name',
|
|
116
|
+
'sample_grids.member', 'id',
|
|
117
|
+
'{"page_size": 10,
|
|
118
|
+
"headings": {"team_id": "Team", "starts": "Started", "hours": "Hours a week", "rate": "Hourly rate"},
|
|
119
|
+
"columns": {"name": {"required": true},
|
|
120
|
+
"team_id": {"lov": "select name, id from sample_grids.team order by name", "required": true},
|
|
121
|
+
"role": {"lov": "STATIC:Lead,Developer,Designer,Tester,Analyst", "required": true}},
|
|
122
|
+
"formats": {"rate": "999G990D00"},
|
|
123
|
+
"aggregates": {"hours": ["sum", "avg"], "rate": "avg", "id": "count"},
|
|
124
|
+
"frozen": 1,
|
|
125
|
+
"hidden": ["id"],
|
|
126
|
+
"row_actions": {"duplicate": true, "delete": true},
|
|
127
|
+
"layout": {"order": ["name", "team_id", "role", "hours", "rate", "starts", "remote"], "widths": {"name": 160}}}', 'members'),
|
|
128
|
+
|
|
129
|
+
-- 4 Teams and members (the wizard's grids follow at seq 10 and 20)
|
|
130
|
+
(4, 5, 'About this page', 'static', 'plain',
|
|
131
|
+
'<p class="lead">The master-detail wizard made this page. The teams grid has <code>select_row</code>: choosing a row puts its key in the hidden item <code>P4_ID</code>. The members grid has <code>master</code>: its query uses <code>:P4_ID</code>, it refreshes in place when another team is chosen, and its new rows get that team.</p>',
|
|
132
|
+
null, null, '{}', null),
|
|
133
|
+
|
|
134
|
+
-- 5 Reading and analysing
|
|
135
|
+
(5, 5, 'About this page', 'static', 'plain',
|
|
136
|
+
'<p class="lead">This grid has no <code>grid_dml</code> process and <code>allow</code> switches off insert, update and delete, so it only reads. Its <strong>views</strong> are still there: open a row in the <em>Single row view</em> (the row menu or Actions), or set up a <em>Group by</em> or a <em>Chart</em> under Actions, for example the hours per team.</p>',
|
|
137
|
+
null, null, '{}', null),
|
|
138
|
+
(5, 10, 'Members per team', 'grid', 'standard',
|
|
139
|
+
E'select m.id, m.name, t.name as team, m.role, m.hours, m.rate, m.remote\n from sample_grids.member m join sample_grids.team t on t.id = m.team_id\n order by t.name, m.name',
|
|
140
|
+
'sample_grids.member', 'id',
|
|
141
|
+
'{"page_size": 25, "allow": {"insert": false, "update": false, "delete": false}, "views": ["row", "group", "chart"],
|
|
142
|
+
"hidden": ["id"], "aggregates": {"hours": "sum"}, "row_actions": {}}', 'analysis')
|
|
143
|
+
) as r (page, seq, title, type, template, source, tab, pk, config, sid);
|
|
144
|
+
|
|
145
|
+
insert into meta.process (page_id, seq, name, type, region_id)
|
|
146
|
+
select pg_temp.page_id('sample-grids', 3), 10, 'Save the members', 'grid_dml', r.id
|
|
147
|
+
from meta.region r where r.page_id = pg_temp.page_id('sample-grids', 3) and r.static_id = 'members';
|
|
148
|
+
|
|
149
|
+
-- the detail grid of page 4: the role as a select list
|
|
150
|
+
update meta.region set config = jsonb_set(config, '{columns,role}', '{"lov": "STATIC:Lead,Developer,Designer,Tester,Analyst", "required": true}')
|
|
151
|
+
where page_id = pg_temp.page_id('sample-grids', 4) and seq = 20;
|
|
@@ -0,0 +1,151 @@
|
|
|
1
|
+
-- =====================================================================
|
|
2
|
+
-- Sample app "Sample Reporting" (App Gallery → Sample Apps)
|
|
3
|
+
--
|
|
4
|
+
-- Reports and the ways to search them: an interactive report with its form,
|
|
5
|
+
-- a faceted search, smart filters, and a classic report with column
|
|
6
|
+
-- display options, a column template and a link.
|
|
7
|
+
--
|
|
8
|
+
-- Installed by an administrator from the App Gallery, or with
|
|
9
|
+
-- psql "$DATABASE_URL" -1 -f examples/gallery/samples/sample-reporting.sql
|
|
10
|
+
-- then open /a/sample-reporting and sign in as demo / demo.
|
|
11
|
+
-- =====================================================================
|
|
12
|
+
|
|
13
|
+
create or replace function pg_temp.page_id(p_alias text, p_page int) returns int language sql stable as
|
|
14
|
+
$$ select p.id from meta.page p join meta.app a on a.id = p.app_id where a.alias = p_alias and p.page_no = p_page $$;
|
|
15
|
+
create or replace function pg_temp.region_id(p_alias text, p_page int, p_static text) returns int language sql stable as
|
|
16
|
+
$$ select r.id from meta.region r where r.page_id = pg_temp.page_id(p_alias, p_page) and r.static_id = p_static $$;
|
|
17
|
+
|
|
18
|
+
-- 1. Schema and data ---------------------------------------------------------
|
|
19
|
+
create schema sample_reporting;
|
|
20
|
+
|
|
21
|
+
create table sample_reporting.book (
|
|
22
|
+
id int generated always as identity primary key,
|
|
23
|
+
title text not null,
|
|
24
|
+
author text not null,
|
|
25
|
+
genre text not null check (genre in ('Novel', 'Crime', 'Science fiction', 'History', 'Science', 'Poetry')),
|
|
26
|
+
language text not null default 'English',
|
|
27
|
+
published int not null check (published between 1500 and 2100),
|
|
28
|
+
pages int,
|
|
29
|
+
rating int check (rating between 1 and 5),
|
|
30
|
+
price numeric(8, 2),
|
|
31
|
+
in_stock boolean not null default true
|
|
32
|
+
);
|
|
33
|
+
|
|
34
|
+
insert into sample_reporting.book (title, author, genre, language, published, pages, rating, price, in_stock) values
|
|
35
|
+
('Pride and Prejudice', 'Jane Austen', 'Novel', 'English', 1813, 432, 5, 9.99, true),
|
|
36
|
+
('Middlemarch', 'George Eliot', 'Novel', 'English', 1871, 880, 4, 12.50, true),
|
|
37
|
+
('Max Havelaar', 'Multatuli', 'Novel', 'Dutch', 1860, 352, 4, 11.00, false),
|
|
38
|
+
('The Hound of the Baskervilles', 'Arthur Conan Doyle', 'Crime', 'English', 1902, 256, 4, 7.95, true),
|
|
39
|
+
('Murder on the Orient Express', 'Agatha Christie', 'Crime', 'English', 1934, 274, 5, 8.99, true),
|
|
40
|
+
('Maigret and the Yellow Dog', 'Georges Simenon', 'Crime', 'French', 1931, 160, 3, 9.50, true),
|
|
41
|
+
('The Time Machine', 'H. G. Wells', 'Science fiction', 'English', 1895, 118, 4, 6.50, true),
|
|
42
|
+
('Foundation', 'Isaac Asimov', 'Science fiction', 'English', 1951, 255, 5, 10.99, false),
|
|
43
|
+
('Solaris', 'Stanisław Lem', 'Science fiction', 'Polish', 1961, 204, 4, 11.95, true),
|
|
44
|
+
('The Guns of August', 'Barbara Tuchman', 'History', 'English', 1962, 640, 5, 15.00, true),
|
|
45
|
+
('Homo Ludens', 'Johan Huizinga', 'History', 'Dutch', 1938, 288, 3, 13.50, true),
|
|
46
|
+
('The Waning of the Middle Ages', 'Johan Huizinga', 'History', 'Dutch', 1919, 400, 4, 14.00, false),
|
|
47
|
+
('On the Origin of Species', 'Charles Darwin', 'Science', 'English', 1859, 502, 5, 10.00, true),
|
|
48
|
+
('A Brief History of Time', 'Stephen Hawking', 'Science', 'English', 1988, 212, 4, 12.99, true),
|
|
49
|
+
('The Double Helix', 'James Watson', 'Science', 'English', 1968, 226, 3, 11.50, true),
|
|
50
|
+
('Leaves of Grass', 'Walt Whitman', 'Poetry', 'English', 1855, 384, 4, 9.00, true),
|
|
51
|
+
('Les Fleurs du mal', 'Charles Baudelaire', 'Poetry', 'French', 1857, 300, 5, 8.50, false),
|
|
52
|
+
('Verzamelde gedichten', 'Lucebert', 'Poetry', 'Dutch', 1974, 620, 3, 29.95, true);
|
|
53
|
+
|
|
54
|
+
-- 2. The application's database role ----------------------------------------
|
|
55
|
+
do $$ begin
|
|
56
|
+
if not exists (select 1 from pg_roles where rolname = 'app_sample_reporting') then create role app_sample_reporting nologin; end if;
|
|
57
|
+
end $$;
|
|
58
|
+
grant app_sample_reporting to pgkiln_runtime;
|
|
59
|
+
grant usage on schema sample_reporting to app_sample_reporting;
|
|
60
|
+
grant select, insert, update, delete on all tables in schema sample_reporting to app_sample_reporting;
|
|
61
|
+
|
|
62
|
+
-- 3. The application ----------------------------------------------------------
|
|
63
|
+
insert into meta.app (alias, name, home_page, authentication, db_role)
|
|
64
|
+
values ('sample-reporting', 'Sample Reporting', 1, 'app_users', 'app_sample_reporting');
|
|
65
|
+
|
|
66
|
+
insert into meta.app_user (app_id, username, password_hash, roles)
|
|
67
|
+
select id, 'demo', meta.hash_password('demo'), '{}' from meta.app where alias = 'sample-reporting';
|
|
68
|
+
|
|
69
|
+
insert into meta.page (app_id, page_no, name, title, parent_page)
|
|
70
|
+
select a.id, p.no, p.name, p.name, p.parent
|
|
71
|
+
from meta.app a, (values (1, 'Sample Reporting', null), (5, 'Smart filters', 1), (6, 'Classic report', 1)) as p (no, name, parent)
|
|
72
|
+
where a.alias = 'sample-reporting';
|
|
73
|
+
|
|
74
|
+
-- page 2 and its form, page 3: the Report and form wizard; page 4: the Faceted search wizard
|
|
75
|
+
select meta.generate_page('sample-reporting', 'report_form', 'sample_reporting.book', 2, '{"label": "Books", "form_page": 3, "nav": false}');
|
|
76
|
+
select meta.generate_page('sample-reporting', 'facets', 'sample_reporting.book', 4,
|
|
77
|
+
'{"label": "Faceted search", "nav": false, "facets": ["genre", "language", "rating", "in_stock", "published"]}');
|
|
78
|
+
update meta.page set parent_page = 1 where app_id = (select id from meta.app where alias = 'sample-reporting') and page_no in (2, 4);
|
|
79
|
+
update meta.item set type = 'select', lov = 'STATIC:Novel,Crime,Science fiction,History,Science,Poetry'
|
|
80
|
+
where page_id = pg_temp.page_id('sample-reporting', 3) and name = 'P3_GENRE';
|
|
81
|
+
update meta.item set type = 'rating' where page_id = pg_temp.page_id('sample-reporting', 3) and name = 'P3_RATING';
|
|
82
|
+
|
|
83
|
+
insert into meta.nav_entry (app_id, seq, label, icon, target_page)
|
|
84
|
+
select a.id, n.seq, n.label, n.icon, n.page
|
|
85
|
+
from meta.app a, (values
|
|
86
|
+
(10, 'Home', 'home', 1), (20, 'Interactive report', 'table', 2), (30, 'Faceted search', 'filter', 4),
|
|
87
|
+
(40, 'Smart filters', 'search', 5), (50, 'Classic report', 'list', 6)
|
|
88
|
+
) as n (seq, label, icon, page)
|
|
89
|
+
where a.alias = 'sample-reporting';
|
|
90
|
+
|
|
91
|
+
insert into meta.list (app_id, name, type, description)
|
|
92
|
+
select id, 'REPORT_TOPICS', 'static', 'The pages of Sample Reporting' from meta.app where alias = 'sample-reporting';
|
|
93
|
+
|
|
94
|
+
insert into meta.list_entry (app_id, list_name, seq, label, icon, target_page, description)
|
|
95
|
+
select a.id, 'REPORT_TOPICS', e.seq, e.label, e.icon, e.page, e.description
|
|
96
|
+
from meta.app a, (values
|
|
97
|
+
(10, 'Interactive report', 'table', 2, 'Search, filters, sorting, control breaks, aggregates, highlights, charts, saved reports and downloads.'),
|
|
98
|
+
(20, 'Faceted search', 'filter', 4, 'Filters with counts beside the report, made by the Faceted search wizard.'),
|
|
99
|
+
(30, 'Smart filters', 'search', 5, 'One search field with filter chips and suggestions.'),
|
|
100
|
+
(40, 'Classic report', 'list', 6, 'A report without the search and Actions menu, with column display options and a column template.')
|
|
101
|
+
) as e (seq, label, icon, page, description)
|
|
102
|
+
where a.alias = 'sample-reporting';
|
|
103
|
+
|
|
104
|
+
insert into meta.region (page_id, seq, title, type, columns, template, source, config, static_id)
|
|
105
|
+
select pg_temp.page_id('sample-reporting', r.page), r.seq, r.title, r.type, r.cols, r.template, r.source, r.config::jsonb, r.sid
|
|
106
|
+
from (values
|
|
107
|
+
-- 1 Home
|
|
108
|
+
(1, 10, 'Sample Reporting', 'static', 12, 'hero',
|
|
109
|
+
'<p>A report region shows the rows of any SELECT. As an <em>interactive report</em> the users search, filter, sort, group, chart, save and download it themselves; everything they enter becomes literals, columns are checked against the query, and the state lives in the URL, so a report can be bookmarked.</p>',
|
|
110
|
+
'{"icon": "table"}', null),
|
|
111
|
+
(1, 20, 'Topics', 'list', 12, 'plain', null, '{"list": "REPORT_TOPICS", "template": "cards"}', null),
|
|
112
|
+
|
|
113
|
+
-- 2 Interactive report (the wizard's report follows at seq 10)
|
|
114
|
+
(2, 5, 'About this page', 'static', 12, 'plain',
|
|
115
|
+
'<p class="lead">The Report and form wizard made this page: an interactive report with a link to a modal form. Click a column heading for its menu (sort, hide, control break, its values), or open <em>Actions</em> for filters, aggregates, highlights, computed columns, group by, pivot, a chart, saved reports and downloads (CSV, Excel, PDF).</p>',
|
|
116
|
+
'{}', null),
|
|
117
|
+
|
|
118
|
+
-- 4 Faceted search (the wizard's regions follow)
|
|
119
|
+
(4, 5, 'About this page', 'static', 12, 'plain',
|
|
120
|
+
'<p class="lead">The Faceted search wizard made this page: a <code>facets</code> region in the left column that filters the report beside it. Each value shows how many rows it would leave, counting the search and the other facets.</p>',
|
|
121
|
+
'{}', null),
|
|
122
|
+
|
|
123
|
+
-- 5 Smart filters
|
|
124
|
+
(5, 5, 'About this page', 'static', 12, 'plain',
|
|
125
|
+
'<p class="lead">A <code>smart_filters</code> region is the compact alternative: one search field above the report, the filters in use as chips and suggestions below it. Type <em>dutch</em> or <em>crime</em> and choose a suggestion.</p>',
|
|
126
|
+
'{}', null),
|
|
127
|
+
(5, 10, 'Search', 'smart_filters', 12, 'plain', null, '{}', 'smart'),
|
|
128
|
+
(5, 20, 'Books', 'report', 12, 'standard',
|
|
129
|
+
E'select title, author, genre, language, published, rating, price from sample_reporting.book order by title',
|
|
130
|
+
'{"formats": {"price": "999G990D00"}}', 'smart-report'),
|
|
131
|
+
|
|
132
|
+
-- 6 Classic report
|
|
133
|
+
(6, 5, 'About this page', 'static', 12, 'plain',
|
|
134
|
+
'<p class="lead">With <code>"searchable": false</code> a report is a classic report: no search field and no Actions menu. <code>column_options</code> sets how columns look (bold, muted, monospace, right-aligned), <code>column_templates</code> draws the stock column through the built-in badge component, and <code>link</code> makes the title open the form.</p>',
|
|
135
|
+
'{}', null),
|
|
136
|
+
(6, 10, 'Books per genre', 'report', 12, 'standard',
|
|
137
|
+
E'select id, title, author, genre, published, price,\n case when in_stock then ''In stock'' else ''Sold out'' end as stock,\n case when in_stock then ''success'' else ''danger'' end as stock_state\n from sample_reporting.book order by genre, title',
|
|
138
|
+
'{"searchable": false, "page_size": 50, "hidden": ["id", "stock_state"],
|
|
139
|
+
"link": {"column": "title", "page": 3, "items": {"P3_ID": "#id#"}},
|
|
140
|
+
"formats": {"price": "999G990D00"},
|
|
141
|
+
"column_options": {"title": ["to-col-bold"], "author": ["to-col-muted"], "published": ["to-col-mono"], "price": ["to-col-right"]},
|
|
142
|
+
"column_templates": {"stock": {"component": "ut_badge", "attributes": {"LABEL": "#stock#", "STATE": "#stock_state#"}}}}', null)
|
|
143
|
+
) as r (page, seq, title, type, cols, template, source, config, sid);
|
|
144
|
+
|
|
145
|
+
update meta.region set config = jsonb_build_object(
|
|
146
|
+
'report', pg_temp.region_id('sample-reporting', 5, 'smart-report'),
|
|
147
|
+
'placeholder', 'Search or filter the books…',
|
|
148
|
+
'facets', '[{"column": "genre", "label": "Genre"}, {"column": "language", "label": "Language"},
|
|
149
|
+
{"column": "published", "label": "Published", "type": "range",
|
|
150
|
+
"ranges": [{"to": 1900}, {"from": 1900, "to": 1950}, {"from": 1950}]}]'::jsonb)
|
|
151
|
+
where id = pg_temp.region_id('sample-reporting', 5, 'smart');
|