pgkiln 0.50.0 → 0.52.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 +7 -0
- package/db/migrations/119_workspace_home.sql +30 -0
- package/db/migrations/120_datasets.sql +87 -0
- package/db/migrations/121_ai_agents.sql +94 -0
- package/db/migrations/122_team_development.sql +186 -0
- package/db/migrations/123_workflow_outcomes.sql +407 -0
- package/db/migrations/124_component_details.sql +51 -0
- package/docs/apex-feature-parity.md +15 -12
- package/docs/guide/01-installation.md +9 -3
- package/docs/guide/03-builder.md +211 -11
- package/docs/guide/04-pages-and-regions.md +96 -22
- package/docs/guide/05-items.md +7 -1
- package/docs/guide/06-processing.md +123 -8
- package/docs/guide/07-dynamic-actions.md +34 -0
- package/docs/guide/09-reference.md +2 -1
- package/docs/guide/11-from-apex.md +2 -1
- package/docs/guide/12-development.md +70 -11
- package/docs/guide/20-ai-agents.md +4 -2
- package/examples/hr/README.md +2 -0
- package/examples/hr/hr_63_datasets.sql +29 -0
- package/examples/hr/hr_64_ai_agent.sql +97 -0
- package/examples/hr/hr_65_ask_reports.sql +21 -0
- package/examples/hr/hr_66_team_development.sql +58 -0
- package/examples/hr/hr_67_workflow_outcomes.sql +113 -0
- package/examples/hr/hr_68_component_details.sql +96 -0
- package/package.json +1 -1
- package/public/app.css +46 -0
- package/public/app.js +134 -20
- package/public/builder.css +116 -0
- package/public/builder.js +47 -0
- package/src/ai/chat.ts +18 -18
- package/src/ai/service.ts +35 -11
- package/src/ai/types.ts +7 -2
- package/src/app.ts +11 -1
- package/src/builder/advisor.ts +6 -3
- package/src/builder/ai-agents.ts +372 -0
- package/src/builder/ai-builder.ts +3 -3
- package/src/builder/ai.ts +13 -9
- package/src/builder/assistant.ts +22 -7
- package/src/builder/components.ts +38 -9
- package/src/builder/datasets.ts +472 -0
- package/src/builder/designer.ts +8 -3
- package/src/builder/feedback.ts +6 -1
- package/src/builder/home.ts +12 -7
- package/src/builder/instance.ts +2 -0
- package/src/builder/locks.ts +7 -1
- package/src/builder/region-help.ts +2 -2
- package/src/builder/reporter.ts +52 -3
- package/src/builder/routes.ts +7 -4
- package/src/builder/shared.ts +1 -1
- package/src/builder/subscriptions.ts +1 -0
- package/src/builder/team.ts +991 -0
- package/src/builder/ui.ts +8 -5
- package/src/builder/workflow-migrate.ts +210 -0
- package/src/builder/workflows.ts +29 -6
- package/src/builder/workspace-home.ts +285 -0
- package/src/builder/workspace-transfer.ts +1 -1
- package/src/builder/workspaces.ts +7 -4
- package/src/cli/replace.ts +7 -1
- package/src/datasets.ts +367 -0
- package/src/i18n/ar.ts +33 -0
- package/src/i18n/cs.ts +33 -0
- package/src/i18n/da.ts +33 -0
- package/src/i18n/de.ts +33 -0
- package/src/i18n/el.ts +33 -0
- package/src/i18n/es.ts +33 -0
- package/src/i18n/fi.ts +33 -0
- package/src/i18n/fr.ts +33 -0
- package/src/i18n/he.ts +33 -0
- package/src/i18n/it.ts +33 -0
- package/src/i18n/ja.ts +33 -0
- package/src/i18n/ko.ts +33 -0
- package/src/i18n/nb.ts +33 -0
- package/src/i18n/pl.ts +33 -0
- package/src/i18n/pt.ts +33 -0
- package/src/i18n/ru.ts +33 -0
- package/src/i18n/sv.ts +33 -0
- package/src/i18n/tr.ts +33 -0
- package/src/i18n/uk.ts +33 -0
- package/src/i18n/zh.ts +33 -0
- package/src/i18n.ts +66 -0
- package/src/instance.ts +3 -0
- package/src/metadata.ts +9 -3
- package/src/release-check.ts +139 -0
- package/src/reporting-app.ts +101 -0
- package/src/runtime/ai-agents.ts +365 -0
- package/src/runtime/ai-filter.ts +385 -107
- package/src/runtime/assistant.ts +64 -11
- package/src/runtime/context.ts +2 -0
- package/src/runtime/data-reporter.ts +199 -32
- package/src/runtime/ir-phrases.ts +812 -0
- package/src/runtime/ir-plan.ts +449 -0
- package/src/runtime/items.ts +1 -1
- package/src/runtime/regions.ts +6 -2
- package/src/runtime/render.ts +1 -1
- package/src/runtime/report.ts +1 -1
- package/src/runtime/tasks.ts +63 -12
- package/src/session.ts +2 -0
- package/src/subscriptions.ts +2 -1
- package/src/task-events.ts +232 -0
- package/src/team-samples.ts +74 -0
- package/src/team.ts +342 -0
- package/src/workflow.ts +135 -18
- package/src/workspace-export.ts +39 -4
|
@@ -21,6 +21,9 @@ src/
|
|
|
21
21
|
env.ts .env loader (imported first)
|
|
22
22
|
migrate.ts applies db/migrations and examples (scripts/migrate.ts, pgkiln migrate); logs each run that applies
|
|
23
23
|
instance.ts instance settings (meta.setting over environment variables, cached 30 s) and the configuration overview
|
|
24
|
+
release-check.ts (sprint 109) "pgkiln X is available": meta.setting 'release_check' over PGKILN_RELEASE_CHECK (off by default);
|
|
25
|
+
GitHub's releases/latest (PGKILN_RELEASE_URL in tests), 5 s timeout, 1 MB, cached a day per process,
|
|
26
|
+
never under node --test without PGKILN_RELEASE_URL; parseRelease keeps a semver and an https link only
|
|
24
27
|
or fails a file in public.pgkiln_install_log
|
|
25
28
|
appfiles.ts application export as one file per component (dir layout, static ids) and back
|
|
26
29
|
blueprint.ts blueprints (migration 063): checkBlueprint (names, types, references, pages, sample rows), tableOrder,
|
|
@@ -30,6 +33,10 @@ src/
|
|
|
30
33
|
rows and columns from the response schema, write-back from collection and item paths, notes), catalog
|
|
31
34
|
files, load/insert/replace, sourceFromService
|
|
32
35
|
workspace-export.ts (sprint 67) a workspace's components as a file (pgkiln-workspace/1) and importWorkspace (one transaction)
|
|
36
|
+
team.ts (sprint 113) Team Development's data: workspaceDevelopers (members and administrators: assignees, @mentions),
|
|
37
|
+
issueHtml (markdownHtml + mentions), recordMentions, logIssue (history), teamSetup, teamCounts, pageIssues,
|
|
38
|
+
exportTeam / importTeam (the workspace export's "team" part)
|
|
39
|
+
team-samples.ts (sprint 113) the set-up wizard's samples per builder language (English; add a key for another)
|
|
33
40
|
subscriptions.ts application types and subscriptions (migration 056): offers, subscribe, refresh, publish, in sync
|
|
34
41
|
workingcopy.ts working copies (migration 055): create, three-way compare per component (base, main, copy),
|
|
35
42
|
merge into the main application or refresh the copy, both through cli/replace.ts
|
|
@@ -52,7 +59,10 @@ src/
|
|
|
52
59
|
customauth.ts custom authentication: the app's function or PL/pgSQL body (a pg_temp function) and post-authentication code, as the app's role
|
|
53
60
|
remember.ts "Keep me signed in": rotating persistent sign-in tokens
|
|
54
61
|
workflow.ts workflows: step checks, the runner with parallel branches (NOTIFY + polling), invoke_api steps
|
|
55
|
-
(the call between two transactions, with a lease), Advisor references, the diagram
|
|
62
|
+
(the call between two transactions, with a lease), Advisor references, the diagram;
|
|
63
|
+
(sprint 114) send_mail and draft steps, switch cases on task outcomes, the diagram as a standalone SVG
|
|
64
|
+
task-events.ts (sprint 114) task events (complete, expire, fail: SQL, mail, start a workflow) and built-in task
|
|
65
|
+
notifications from e-mail templates: checks, the actions as the app role, the meta.task_hook outbox runner
|
|
56
66
|
process-jobs.ts background execution chains: the job queue (SKIP LOCKED, NOTIFY + polling), running a job as the app role
|
|
57
67
|
api.ts REST API tokens for PostgREST, API role checks
|
|
58
68
|
accounts.ts account settings and the password policy
|
|
@@ -77,6 +87,13 @@ src/
|
|
|
77
87
|
datamodel.ts (sprint 98) Data Modeler diagrams: readModel() (catalog), boxSize(), fkPath() (crow's feet), autoLayout(), diagramSvg()
|
|
78
88
|
changelog.ts (sprint 106, migration 118) the change log: isSchemaDdl(), ddlTarget() (schema and object), changeName()
|
|
79
89
|
(add_bonus, create_index_x), recordChanges() (grouped per schema), Flyway / Liquibase / plain SQL writers
|
|
90
|
+
datasets.ts (sprint 110, migration 120) Data Reporter datasets: checkDataset() (names, joins to earlier tables, columns
|
|
91
|
+
and filters on its tables, whitelisted operators; with a catalog: existing objects), datasetSql() (quoted
|
|
92
|
+
identifiers, literal values), readSchemaCatalog() (tables, views, columns, foreign keys), proposeJoin() /
|
|
93
|
+
proposeDataset(), storeDataset(), appDatasets() (the runtime's read), pgkiln-dataset/1 files
|
|
94
|
+
reporting-app.ts (sprint 110) reporting applications: createReportingApp() (createApp, SELECT-only grants on the datasets'
|
|
95
|
+
objects, type reporting, roles viewer/editor/administrator as schemes EDITOR and ADMIN, page 1 a Data
|
|
96
|
+
Reporter), reportingApps(), reportingAppCount() (meta.reporting_app_count, for the workspace home)
|
|
80
97
|
tabledef.ts (sprint 95) a table as a definition: readTable(), checkTable(), tableSql() (CREATE, or ALTER from the catalog's version)
|
|
81
98
|
sqlformat.ts (sprint 93) formatSql(): the worksheet's Format (clauses per line, keyword case; strings and comments kept)
|
|
82
99
|
sqlplan.ts (sprint 92) EXPLAIN (FORMAT JSON) as a tree: parsePlan() (self time, slowest step, misestimates), planText()
|
|
@@ -128,7 +145,10 @@ src/
|
|
|
128
145
|
limits, the call, meta.ai_usage), requests.ts (meta.ai_generate from SQL: runPendingAi() after an sql
|
|
129
146
|
process, aiRequestTick() in the scheduler, 24-hour purge), chat.ts (conversations with tools, migration
|
|
130
147
|
061: chat() runs the tool loop on a provider-format history (Claude: streamed, strict tools, tool_choice
|
|
131
|
-
auto, content appended unchanged; OpenAI: strict function tools), limits and usage per provider call
|
|
148
|
+
auto, content appended unchanged; OpenAI: strict function tools), limits and usage per provider call;
|
|
149
|
+
(121) an agent's effort (Claude output_config.effort, OpenAI reasoning_effort) and output limit).
|
|
150
|
+
Providers (121): anthropic, openai, gemini (OpenAI-compatible endpoint, GEMINI_BASE_URL), openai_compatible
|
|
151
|
+
(Ollama, vLLM, Azure OpenAI; base URL required, key optional): service.ts serviceKey(), baseUrlFor(), providerLabel()
|
|
132
152
|
debug.ts debug messages: DebugLog (levels, timed steps, NOTICEs of meta.debug from appTx), started in
|
|
133
153
|
loadContext, stored after the response (onResponse hook → meta.debug_save), hourly purge
|
|
134
154
|
icons.ts icon helper (sprite in public/icons.svg)
|
|
@@ -146,7 +166,7 @@ src/
|
|
|
146
166
|
styles.ts base styles (BASE_STYLES: Iris, Lavender, Standard; baseStyleOf → html data-style), Theme Roller style variants: fixed lists (fonts, sizes, corners), parseStyle/appStyles checks, the request's
|
|
147
167
|
style (user choice, default), themeCss() (only hex values and constants reach the CSS)
|
|
148
168
|
template-options.ts template options: the fixed CSS class list per region and button, templateClasses() (unknown values ignored)
|
|
149
|
-
regions.ts region shell + chart (drill-down links, gauge settings)/cards/dynamic dispatch with row limits, lazy placeholder and cache, buttons (menu buttons, badges)
|
|
169
|
+
regions.ts region shell + chart (drill-down links, gauge settings)/cards/dynamic dispatch with row limits, lazy placeholder and cache, buttons (menu buttons, badges; (124) menu entries {action} → data-menu-entry for menu_entry dynamic actions)
|
|
150
170
|
report.ts, report-views.ts (group by, pivot, chart), compute.ts (computed column expressions), grid.ts (aggregates, row actions, Actions menu, single row view, layoutFromForm), grid-layout.ts (column layouts: clean, arrange, per user), master-detail.ts (signed master row selection, details), facets.ts (facetChart: a facet's counts as a chart, sprint 65), items.ts
|
|
151
171
|
(report.ts: paging with row ranges and max_rows, keyset paging (keysetPlan, seekCondition, signed r<id>_k), pagerNav, streamed CSV/Excel downloads with a cursor;
|
|
152
172
|
items.ts: lovOptions, searchLov/lovLookup for popup LOVs, served by POST /a/:alias/:page/lov/:item/search in routes.ts)
|
|
@@ -183,9 +203,12 @@ src/
|
|
|
183
203
|
template-components.ts template components: template language (allow-list, directives, escaping), plug-in files, report column templates
|
|
184
204
|
builtin-components.ts built-in template components (ut_avatar, ut_badge, ut_comments, ut_media_list, ut_metric_card, ut_timeline)
|
|
185
205
|
template-region.ts template_component region
|
|
186
|
-
tasks.ts task list region and task actions (approvals); (094) the vacation rule panel and route
|
|
206
|
+
tasks.ts task list region and task actions (approvals); (094) the vacation rule panel and route;
|
|
207
|
+
(sprint 114) custom outcome buttons, the Fail event and recovery of a failing completion SQL
|
|
187
208
|
data-reporter.ts Data Reporter region (migration 057): sources from the region's config, checkDef (offered columns, whitelists),
|
|
188
|
-
reportQuery/chartQuery, the list and editor (GET form), save/delete routes (meta.save_data_report)
|
|
209
|
+
reportQuery/chartQuery, the list and editor (GET form), save/delete routes (meta.save_data_report);
|
|
210
|
+
(sprint 110) dataset sources (resolveSources), edit_authz / admin_authz / publishing, pins (a preference),
|
|
211
|
+
publish / pin routes, the stable URL GET /a/:alias/reports/:id
|
|
189
212
|
form-designer.ts Form Designer region (migration 106): sources from the region's config, checkFormDef (offered columns, displays,
|
|
190
213
|
actions), forms as cards, a form's rows, a row's form (lost update hash), the designer; row/design/delete routes
|
|
191
214
|
workflows.ts workflow console region and its actions
|
|
@@ -193,8 +216,20 @@ src/
|
|
|
193
216
|
schemas from items, answers into items), aiInputs/aiOutputs for its dynamic action (route in routes.ts)
|
|
194
217
|
assistant.ts AI assistant region (migration 061): config checks, tool schemas and argument checks, context queries
|
|
195
218
|
and SQL tools as the app role (rolled back unless "writes"), REST tools, meta.ai_conversation per
|
|
196
|
-
session, formatAnswer (escaped), send/clear routes
|
|
197
|
-
|
|
219
|
+
session, formatAnswer (escaped), send/clear routes; (121) "agent": an AI agent's service, prompt, tools,
|
|
220
|
+
steps and authorization; links of navigate tools under the answer, browser actions once (__AIACT<id>)
|
|
221
|
+
ai-agents.ts (121) AI agents and AI tools (meta.app.ai_agents / ai_tools): checks (agentProblems, aiToolProblems,
|
|
222
|
+
definitionProblems), agentFor(), prepareTools() (authorization schemes, navigate pages the user may
|
|
223
|
+
open, strict schemas), runAgentTool() (sql: read-only transaction as the app role with a time limit;
|
|
224
|
+
function: named arguments, committed; rest; javascript: queued for the browser; navigate: URL and
|
|
225
|
+
checksum built here), logToolCall() (meta.ai_tool_call, debug level 4, 30-day purge)
|
|
226
|
+
ai-filter.ts "Ask in your own words" on a report ("ai_filter", sprint 112: AI interactive reports): the box, the chips
|
|
227
|
+
form (Edit/Remove/Undo, undo history in the session), ask and chips routes; an AI service's structured
|
|
228
|
+
output (planSchema) or the phrase parser, then ir-plan.ts
|
|
229
|
+
ir-plan.ts what a question may set: checkPlan (shown columns, whitelists, computeSql), applyPlan (→ the report's
|
|
230
|
+
URL parameters), chipsOf/editChip/removeChip (a chip = one URL parameter value, edits checked again)
|
|
231
|
+
ir-phrases.ts the rule-based parser without an AI service: phrase tables per language (en, nl, de), clauses →
|
|
232
|
+
a RawPlan; values looked up among the report's rows through a Lookup (query parameter)
|
|
198
233
|
pdf.ts report PDFs with report layouts (pdfkit); rows from a cursor in batches (tablePdf takes batches)
|
|
199
234
|
search.ts search region and search configurations (migration 080): words as ILIKE parameters, table/sql/list
|
|
200
235
|
sources as the app role in savepoints, marked results, configColumns/searchConfigProblems for the Advisor
|
|
@@ -217,7 +252,12 @@ src/
|
|
|
217
252
|
routes.ts sign-in, app home, settings, activity, developers, create (POST, via newapp.ts)/import (POST)
|
|
218
253
|
wizards.ts create page wizards: step 2 forms per page type (defaults from meta.wizard_defaults), POST → meta.generate_page
|
|
219
254
|
(the generators are PL/pgSQL in migration 047: catalog, defaults, form/cards/calendar/chart/map/facets/master-detail)
|
|
220
|
-
home.ts App Builder home (tiles, applications report/cards, Recent
|
|
255
|
+
home.ts App Builder home at /builder/apps and /builder (tiles, applications report/cards, Recent; /builder redirects
|
|
256
|
+
to the Workspace home when meta.workspace.builder_home = 'home'), Create, Import, Dashboard, Utilities
|
|
257
|
+
workspace-home.ts (sprint 109, migration 119) the Workspace home /builder/home, opened after sign-in: tool cards (Data Reporter
|
|
258
|
+
when GET /builder/reporter exists), workspace message edited in place (workspaceAdmin), Summary, Top
|
|
259
|
+
applications / users / developers over meta.developer.home_timeframe, System message (src/release-check.ts),
|
|
260
|
+
POST timeframe, message, release-check; releaseCheckRegion for Instance settings
|
|
221
261
|
newapp.ts creating an application (schema, role app_<alias>, Home page, first user): blank app, from a file, from tables
|
|
222
262
|
appfromfile.ts Create → From a file: upload (src/dataload.ts parsing), proposed table/columns, one transaction:
|
|
223
263
|
app + table + rows (loadRows) + pages (meta.generate_page: report and form, chart, facets)
|
|
@@ -239,6 +279,11 @@ src/
|
|
|
239
279
|
availability.ts Application → Availability (migration 083): status, message, users, build status (Run only is
|
|
240
280
|
enforced by locks.ts blockingLock); availabilityBadge() in the application header
|
|
241
281
|
feedback.ts Application → Feedback (migration 084): the setting, the list with filters, status and answers
|
|
282
|
+
team.ts (sprint 113, migration 122) Builder → Team Development /builder/team: issues (list with filters and saved views,
|
|
283
|
+
board by label group or milestone with a Move form per card, issue page with Markdown, comments, canned
|
|
284
|
+
responses, @mentions, history), labels and groups, milestones, templates, the set-up wizard; Make an issue
|
|
285
|
+
from feedback and developer comments; pageIssuesNote (Page Designer), appIssues (App Home), teamHomeBlock
|
|
286
|
+
(Workspace home). Every id from a request is looked up in the current workspace
|
|
242
287
|
region-help.ts the Attributes (JSON) help per region type, with links to the guide
|
|
243
288
|
mail.ts Workspace utilities → Mail services (administrators: providers, write-only secret, allowed senders,
|
|
244
289
|
applications with From and daily limit, test) and Applications → Activity → Mail (retry, cancel)
|
|
@@ -305,7 +350,11 @@ src/
|
|
|
305
350
|
pwa.ts Settings → Progressive Web App (icon upload)
|
|
306
351
|
themeroller.ts Settings → Theme Roller: style variants (add, edit, rename, delete), default style, users may choose
|
|
307
352
|
subscriptions.ts Shared Components → Subscriptions: subscribe, refresh, unsubscribe, subscribers and publish; the note under a component
|
|
308
|
-
reporter.ts page designer: Data Reporter settings (sources: table or view, offered columns, labels, masks; sharing
|
|
353
|
+
reporter.ts page designer: Data Reporter settings (sources: table or view, offered columns, labels, masks; sharing;
|
|
354
|
+
(sprint 110) a dataset as a source, who may edit, who may moderate, publishing)
|
|
355
|
+
datasets.ts (sprint 110) App Builder → Data Reporter (/builder/reporter): datasets (new: schema, tables; a dataset's
|
|
356
|
+
joins, columns, row filter, query and preview), files (download, export all, import), Create reporting
|
|
357
|
+
application
|
|
309
358
|
form-designer.ts page designer: Form Designer settings (designers' scheme; sources: key, actions, offered columns, labels, lists of values)
|
|
310
359
|
workingcopies.ts Working copies: list and create, compare with differences, merge or refresh with conflict choices, delete
|
|
311
360
|
rest.ts REST module endpoints list and curl example (Shared Components)
|
|
@@ -316,6 +365,8 @@ src/
|
|
|
316
365
|
workspace's applications, the module › template › handler tree, handler forms, Try it (inject with a
|
|
317
366
|
short-lived token), Run only / lock checks
|
|
318
367
|
workflows.ts workflow versions, diagram and instances (Shared Components)
|
|
368
|
+
workflow-migrate.ts (sprint 114) migrating running workflow instances to a newer version (map, preview, apply with
|
|
369
|
+
meta.migrate_workflows) and the diagram as an SVG file (PNG in builder.js)
|
|
319
370
|
process-jobs.ts page designer: the Jobs tab of a background chain process
|
|
320
371
|
template-spec.ts template component property form (Shared Components)
|
|
321
372
|
websources.ts web credentials and REST data sources: property specs, secret status, Test, suggested columns,
|
|
@@ -338,13 +389,17 @@ src/
|
|
|
338
389
|
ai-builder.ts App Builder AI (migration 062): the builder's AI service (meta.builder_ai), SQL Workshop → AI (SQL from a
|
|
339
390
|
question, shown not run; explain), describe tables (meta.ai_table_note, COMMENT ON, AI drafts),
|
|
340
391
|
create pages with AI (proposals checked by checkProposals, created with meta.generate_page)
|
|
341
|
-
assistant.ts page designer: AI assistant settings and a report's "Ask in your own words" (AI service, placeholder)
|
|
392
|
+
assistant.ts page designer: AI assistant settings and a report's "Ask in your own words" (off, built-in phrases or an AI service, placeholder)
|
|
342
393
|
ai.ts Workspace utilities → AI services (administrators: services, write-only encrypted keys, access and
|
|
343
394
|
daily limits per app, Test, usage log) and Activity → AI usage per application
|
|
395
|
+
ai-agents.ts (121) Shared Components → AI Agents: the agent and tool forms (checked before saving, renames follow
|
|
396
|
+
into agents and regions), toolCallTable() (Activity → AI usage → Tool calls), agentOptions()
|
|
344
397
|
supporting.ts supporting objects: review page, running the install/upgrade/deinstall scripts as the app's role in one transaction
|
|
345
398
|
public/
|
|
346
399
|
app.css theme (light/dark, responsive; --font, --font-size, --radius for style variants; template option classes to-*)
|
|
347
|
-
app.js client runtime: dialogs (dialog_closed actions), popup LOVs
|
|
400
|
+
app.js client runtime: dialogs (dialog_closed actions), popup LOVs (sprint 115: the next page loads on scroll through an
|
|
401
|
+
IntersectionObserver, ‹/More without one), dynamic actions (focus, classes, messages; sprint 115: wait_popup /
|
|
402
|
+
remove_wait_popup as a modal <dialog class="wait-popup">, pgkiln.waitPopup(), the menu_entry event), grids (add/duplicate rows, master-detail refresh, move/resize columns, copy/paste of cell ranges), menus, lazy regions, maps (Leaflet layers, marker clusters, heat layer, layer legend, area/distance filter) (no inline JS)
|
|
348
403
|
code-editor.js, .css builder code editor: enhances <textarea data-code>, highlighting, suggestions (no dependencies);
|
|
349
404
|
(sprint 108) the current line, and read-only <pre data-code-view="sql|plpgsql|json|html"> highlighted;
|
|
350
405
|
its colours are the --ce-* tokens in builder.css
|
|
@@ -389,6 +444,8 @@ test/
|
|
|
389
444
|
worksheet.test.ts (sprint 91) the SQL Worksheet: statement at the cursor, Run Script log, plans, notices, history, navigator, Run as
|
|
390
445
|
worksheet-txn.test.ts (sprint 107) transaction mode: the tab's transaction, Commit / Rollback, typed COMMIT, aborted, idle timeout, refusals,
|
|
391
446
|
Explain in it, Change Log at COMMIT, the JSON endpoint, sign-out
|
|
447
|
+
datasets.test.ts (sprint 110) datasets: checks and SQL, proposed joins, the builder's pages, files, the workspace export;
|
|
448
|
+
a reporting application in two clicks, an editor publishes, a viewer opens the stable URL, pins
|
|
392
449
|
quicksql.test.ts Quick SQL parser and DDL generator
|
|
393
450
|
xml.test.ts XML reader: rows, attributes, paths, refused DTDs and entities, limits
|
|
394
451
|
printing.test.ts report PDFs
|
|
@@ -403,6 +460,8 @@ test/
|
|
|
403
460
|
rest-sources.test.ts REST data sources, web credentials, SSRF checks, invoke_api (mock service + HR page 23)
|
|
404
461
|
rest-modules.test.ts (sprint 104) modules, templates, PL/pgSQL and media handlers, OpenAPI, the builder's tree and Try it
|
|
405
462
|
workflow-invoke.test.ts workflow invoke_api steps: the call between transactions, faults, retry, lease, Advisor, export (mock service)
|
|
463
|
+
workflow-outcomes.test.ts (sprint 114) custom task outcomes, task events and notifications, fail recovery, send_mail and draft
|
|
464
|
+
steps, migrating running instances, the SVG export (HR part 67: SALARY_CHANGE)
|
|
406
465
|
large-tables.test.ts row ranges, max_rows, row limits, lazy regions, region caching, streamed downloads (HR page 25)
|
|
407
466
|
grid.test.ts interactive grid: aggregates, layouts per user, saved grid reports, master-detail, row actions (HR page 27)
|
|
408
467
|
custom-auth.test.ts custom authentication: function body, named function, post-authentication code, builder settings
|
|
@@ -7,8 +7,10 @@ it exports the application as files, edits them with its own file tools, shows t
|
|
|
7
7
|
and imports them again. Laravel developers know the idea from Laravel Boost.
|
|
8
8
|
|
|
9
9
|
This is separate from the AI features *inside* applications and the builder (AI assistant regions,
|
|
10
|
-
*Generate with AI*): those call a
|
|
11
|
-
pgkiln.
|
|
10
|
+
[AI agents with tools](03-builder.md#ai-agents) in Shared Components, *Generate with AI*): those call a
|
|
11
|
+
model from pgkiln; here an agent on the developer's machine calls pgkiln. An application's AI agents
|
|
12
|
+
and tools are part of its files (`app.json`: `ai_agents`, `ai_tools`), so a coding agent can read and
|
|
13
|
+
change them like any other component.
|
|
12
14
|
|
|
13
15
|
## Setting it up
|
|
14
16
|
|
package/examples/hr/README.md
CHANGED
|
@@ -40,6 +40,8 @@ Then open http://127.0.0.1:3100/a/hr and sign in as `king`, `blake`, `jones`, `a
|
|
|
40
40
|
| `hr_47_plugins.sql` | *Plug-ins* (page 40): the four example plug-ins of `examples/plugins/` (a region, an item, a dynamic action and a process plug-in) |
|
|
41
41
|
| `hr_48_map_tiles.sql` | *Weather stations* (page 41): 20,000 generated stations (`hr.weather_station`) as vector tiles, and the stations above 2,000 m loaded for the visible area |
|
|
42
42
|
| `hr_49_push_notifications.sql` | Push notifications: the app may send them (users turn them on under *My account → Notifications*); a new leave request notifies the manager, a decision notifies the employee, and the notification opens the request (page 7) |
|
|
43
|
+
| `hr_64_ai_agent.sql` | *HR agent* (page 46): the AI agent `HR_AGENT` (Shared Components → AI Agents) with four tools: `top_earners` (SQL, read-only as `hr_app`), `open_employee` (navigate: a link to the employee form with its checksum, only for users who may open page 3), `give_raise` (the PL/pgSQL function `hr.give_raise`, managers only) and `note` (a JavaScript tool: the custom event `hr_agent_note`). Ask *Who in Sales earns most?* Needs an AI service named `HR_ASSISTANT` (see part 41) |
|
|
44
|
+
| `hr_68_component_details.sql` | *Component details* (page 47): a popup LOV over 6,000 rows that loads more as the dialog scrolls, a wait popup around a two-second process (*Recalculate*) and around an Ajax call (*Check in the background*), and an *Actions* menu whose entries run dynamic actions (event `menu_entry`) |
|
|
43
45
|
|
|
44
46
|
The files run in order, each once (recorded in `public.pgkiln_seed`), so new ones can be added
|
|
45
47
|
later. pgkiln's own tests use this application as their fixture (`npm test` installs it first).
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
-- =====================================================================
|
|
2
|
+
-- HR example, part 63: a Data Reporter dataset (docs/guide/04-pages-and-
|
|
3
|
+
-- regions.md, "Datasets and reporting applications")
|
|
4
|
+
--
|
|
5
|
+
-- The dataset "hr" of the Default workspace: employees (hr.emp) joined to
|
|
6
|
+
-- their department (hr.dept, a left join on the foreign key deptno), the
|
|
7
|
+
-- columns business users may see with labels, format masks and
|
|
8
|
+
-- descriptions, and a row filter: active employees only. In the builder,
|
|
9
|
+
-- App Builder → Data Reporter → Create reporting application makes a
|
|
10
|
+
-- reporting application on it in two clicks; datasets are installation
|
|
11
|
+
-- data (a file of their own, and the workspace export), not part of the
|
|
12
|
+
-- HR application's export.
|
|
13
|
+
-- =====================================================================
|
|
14
|
+
|
|
15
|
+
insert into meta.dataset (workspace_id, name, label, description, schema_name, tables, columns, filters, created_by)
|
|
16
|
+
values (1, 'hr', 'HR', 'Active employees with their department.', 'hr',
|
|
17
|
+
'[{"alias": "t1", "table": "emp"},
|
|
18
|
+
{"alias": "t2", "table": "dept", "join": {"to": "t1", "type": "left", "on": [{"column": "deptno", "ref": "deptno"}]}}]',
|
|
19
|
+
'[{"name": "empno", "table": "t1", "column": "empno", "label": "No."},
|
|
20
|
+
{"name": "ename", "table": "t1", "column": "ename", "label": "Name"},
|
|
21
|
+
{"name": "job", "table": "t1", "column": "job", "label": "Job"},
|
|
22
|
+
{"name": "hiredate", "table": "t1", "column": "hiredate", "label": "Hire date"},
|
|
23
|
+
{"name": "sal", "table": "t1", "column": "sal", "label": "Salary", "format": "FML999G990D00", "description": "Monthly salary"},
|
|
24
|
+
{"name": "comm", "table": "t1", "column": "comm", "label": "Commission", "format": "FML999G990D00"},
|
|
25
|
+
{"name": "dname", "table": "t2", "column": "dname", "label": "Department"},
|
|
26
|
+
{"name": "loc", "table": "t2", "column": "loc", "label": "Location", "description": "Where the department is"}]',
|
|
27
|
+
'[{"table": "t1", "column": "active", "op": "eq", "value": "true"}]',
|
|
28
|
+
'admin')
|
|
29
|
+
on conflict (workspace_id, name) do nothing;
|
|
@@ -0,0 +1,97 @@
|
|
|
1
|
+
-- =====================================================================
|
|
2
|
+
-- HR example, part 64: an AI agent with tools (sprint 111;
|
|
3
|
+
-- docs/guide/03-builder.md, "AI agents"; APEX 26.1: AI Agents)
|
|
4
|
+
--
|
|
5
|
+
-- Shared Components → AI Agents:
|
|
6
|
+
-- - Tools:
|
|
7
|
+
-- top_earners (sql) employees by salary, highest first,
|
|
8
|
+
-- optionally of one department: a query
|
|
9
|
+
-- run as hr_app (row level security
|
|
10
|
+
-- applies) in a read-only transaction
|
|
11
|
+
-- open_employee (navigate) a link to the employee form (page 3)
|
|
12
|
+
-- with P3_EMPNO: pgkiln offers it only
|
|
13
|
+
-- to users who may open page 3
|
|
14
|
+
-- (MANAGER) and builds the checksum
|
|
15
|
+
-- give_raise (function) hr.give_raise(p_empno, p_pct): changes
|
|
16
|
+
-- data, only offered to managers
|
|
17
|
+
-- note (javascript) shows a message in the browser after
|
|
18
|
+
-- the answer (the custom event
|
|
19
|
+
-- "hr_agent_note" on page 46)
|
|
20
|
+
-- - Agent HR_AGENT: the AI service HR_ASSISTANT, a system prompt, low
|
|
21
|
+
-- reasoning effort, the four tools, at most 4 steps per question.
|
|
22
|
+
--
|
|
23
|
+
-- Page 46 "HR agent": an AI assistant region that names the agent. Ask
|
|
24
|
+
-- e.g. "Who in Sales earns most?": the agent calls top_earners and offers
|
|
25
|
+
-- "Open BLAKE's record", a link to page 3 with a valid checksum.
|
|
26
|
+
--
|
|
27
|
+
-- Like parts 41 and 42 the example creates no AI service: an administrator
|
|
28
|
+
-- adds one named HR_ASSISTANT under App Builder → Workspace utilities → AI
|
|
29
|
+
-- services (Claude, OpenAI, Gemini or an OpenAI-compatible endpoint) and
|
|
30
|
+
-- allows the HR application to use it.
|
|
31
|
+
-- =====================================================================
|
|
32
|
+
|
|
33
|
+
update meta.app set
|
|
34
|
+
ai_tools = $j$[
|
|
35
|
+
{"name": "top_earners", "type": "sql",
|
|
36
|
+
"description": "Active employees with their job, department and salary, highest salary first; optionally only one department (case-insensitive name, e.g. SALES).",
|
|
37
|
+
"sql": "select empno, ename, job, dname, sal from hr.staff_v where active and (:DNAME is null or dname ilike :DNAME) order by sal desc, ename",
|
|
38
|
+
"parameters": {"DNAME": {"type": "string", "optional": true, "description": "A department name, e.g. SALES."}},
|
|
39
|
+
"max_rows": 20, "timeout_s": 10},
|
|
40
|
+
{"name": "open_employee", "type": "navigate",
|
|
41
|
+
"description": "Offer the user a link to an employee's record (the employee form). Use the employee number (empno) as P3_EMPNO and a label such as \"Open BLAKE's record\".",
|
|
42
|
+
"pages": [3]},
|
|
43
|
+
{"name": "give_raise", "type": "function",
|
|
44
|
+
"description": "Raise an employee's salary by a percentage. Only when the user clearly asks for it; repeat the employee and the percentage first.",
|
|
45
|
+
"function": "hr.give_raise",
|
|
46
|
+
"parameters": {"P_EMPNO": {"type": "integer", "description": "The employee number (empno)."},
|
|
47
|
+
"P_PCT": {"type": "number", "description": "The raise in percent, e.g. 5."}},
|
|
48
|
+
"authz": "MANAGER"},
|
|
49
|
+
{"name": "note", "type": "javascript", "action": "hr_agent_note",
|
|
50
|
+
"description": "Show the user a short confirmation message in the page, e.g. after a raise.",
|
|
51
|
+
"parameters": {"TEXT": {"type": "string", "description": "The message."}}}
|
|
52
|
+
]$j$::jsonb,
|
|
53
|
+
ai_agents = $j$[
|
|
54
|
+
{"name": "HR_AGENT", "description": "Answers questions about salaries and opens employee records",
|
|
55
|
+
"service": "HR_ASSISTANT",
|
|
56
|
+
"system": "You are the HR agent of a small company, talking with &APP_USER.. Answer questions about employees, departments and salaries with the top_earners tool. When you name one employee, offer a link to their record with open_employee (the empno as P3_EMPNO). Be brief.",
|
|
57
|
+
"effort": "low", "tools": ["top_earners", "open_employee", "give_raise", "note"], "max_steps": 4, "max_tokens": 2000}
|
|
58
|
+
]$j$::jsonb
|
|
59
|
+
where alias = 'hr';
|
|
60
|
+
|
|
61
|
+
insert into meta.page (app_id, page_no, name, title, parent_page)
|
|
62
|
+
select id, 46, 'HR agent', 'HR agent', 1 from meta.app where alias = 'hr';
|
|
63
|
+
|
|
64
|
+
insert into meta.region (page_id, seq, title, type, columns, template, source, condition, config)
|
|
65
|
+
select p.id, r.seq, r.title, r.type, r.cols, 'standard', r.source, r.cond, r.config::jsonb
|
|
66
|
+
from meta.page p join meta.app a on a.id = p.app_id, (values
|
|
67
|
+
(10, 'No AI service yet', 'static', 12,
|
|
68
|
+
'<p class="alert alert-info">No AI service is configured for this application yet. An administrator adds an AI service named <b>HR_ASSISTANT</b> under <i>App Builder → Workspace utilities → AI services</i> and allows the HR application to use it.</p>',
|
|
69
|
+
'not meta.ai_available(''HR_ASSISTANT'')', '{}'),
|
|
70
|
+
(20, 'Ask the HR agent', 'ai_assistant', 8, null, 'meta.ai_available(''HR_ASSISTANT'')',
|
|
71
|
+
$j${"agent": "HR_AGENT",
|
|
72
|
+
"welcome": "Hello! Ask me who earns what, e.g. who in Sales earns most. I can open an employee's record for you.",
|
|
73
|
+
"placeholder": "e.g. Who in Sales earns most?",
|
|
74
|
+
"max_turns": 20,
|
|
75
|
+
"error_message": "The HR agent could not answer just now. Please try again later."}$j$)
|
|
76
|
+
) as r (seq, title, type, cols, source, cond, config)
|
|
77
|
+
where a.alias = 'hr' and p.page_no = 46;
|
|
78
|
+
|
|
79
|
+
-- the javascript tool "note": a custom event the agent raises after its answer
|
|
80
|
+
insert into meta.dynamic_action (page_id, seq, name, event, trigger_element, action, message)
|
|
81
|
+
select p.id, 10, 'Agent note', 'custom', 'hr_agent_note', 'show_success', 'The HR agent did what you asked.'
|
|
82
|
+
from meta.page p join meta.app a on a.id = p.app_id where a.alias = 'hr' and p.page_no = 46;
|
|
83
|
+
|
|
84
|
+
insert into meta.nav_entry (app_id, seq, label, icon, target_page)
|
|
85
|
+
select id, 30, 'HR agent', 'users', 46 from meta.app where alias = 'hr';
|
|
86
|
+
|
|
87
|
+
insert into meta.translation (app_id, language, source, target)
|
|
88
|
+
select a.id, 'nl', t.source, t.target
|
|
89
|
+
from meta.app a, (values
|
|
90
|
+
('HR agent', 'HR-agent'),
|
|
91
|
+
('Ask the HR agent', 'Vraag het de HR-agent'),
|
|
92
|
+
('Hello! Ask me who earns what, e.g. who in Sales earns most. I can open an employee''s record for you.',
|
|
93
|
+
'Hallo! Vraag me wie wat verdient, bijv. wie in Sales het meest verdient. Ik kan ook de gegevens van een medewerker voor je openen.'),
|
|
94
|
+
('e.g. Who in Sales earns most?', 'bijv. Wie verdient het meest in Sales?')
|
|
95
|
+
) as t (source, target)
|
|
96
|
+
where a.alias = 'hr'
|
|
97
|
+
on conflict do nothing;
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
-- =====================================================================
|
|
2
|
+
-- HR example, part 65: "Ask in your own words" without an AI service
|
|
3
|
+
-- (docs/guide/04-pages-and-regions.md, "Ask in your own words")
|
|
4
|
+
--
|
|
5
|
+
-- Page 2 "Employees": the interactive report gets a question box with the
|
|
6
|
+
-- built-in phrases ("ai_filter": {} names no AI service). Try
|
|
7
|
+
-- sum of salary by department where job is clerk
|
|
8
|
+
-- salary over 2500, sort by hire date, newest first
|
|
9
|
+
-- only clerks hired after 1981
|
|
10
|
+
-- bonus is 10% of salary
|
|
11
|
+
-- highlight salary over 4000 in red
|
|
12
|
+
-- Each answer becomes the report's own settings (filters, sort, group by,
|
|
13
|
+
-- computed columns, …), shown as chips with Edit and Remove under the box;
|
|
14
|
+
-- Undo goes back a step. Page 38's staff list uses the HR_ASSISTANT AI
|
|
15
|
+
-- service when an administrator has added it, else the same phrases.
|
|
16
|
+
-- =====================================================================
|
|
17
|
+
|
|
18
|
+
update meta.region r
|
|
19
|
+
set config = r.config || '{"ai_filter": {"placeholder": "e.g. sum of salary by department where job is clerk"}}'
|
|
20
|
+
from meta.page p, meta.app a
|
|
21
|
+
where r.page_id = p.id and p.app_id = a.id and a.alias = 'hr' and p.page_no = 2 and r.type = 'report';
|
|
@@ -0,0 +1,58 @@
|
|
|
1
|
+
-- =====================================================================
|
|
2
|
+
-- HR example, part 66: Team Development (docs/guide/03-builder.md,
|
|
3
|
+
-- "Team Development")
|
|
4
|
+
--
|
|
5
|
+
-- What Builder → Team Development → Set up installs in the Default
|
|
6
|
+
-- workspace (the label groups Type and Priority, milestones 1.0 and 1.1,
|
|
7
|
+
-- a bug report template and a canned response), and one issue about the
|
|
8
|
+
-- HR application: "Salary rounding on page 12", a Bug of milestone 1.1,
|
|
9
|
+
-- assigned to admin. The Page Designer of page 12 shows it, and the
|
|
10
|
+
-- Workspace home counts it until it is closed. Issues are workspace data
|
|
11
|
+
-- (the workspace export), not part of the HR application's export.
|
|
12
|
+
-- =====================================================================
|
|
13
|
+
|
|
14
|
+
insert into meta.team_label_group (workspace_id, name, description, seq) values
|
|
15
|
+
(1, 'Type', 'What kind of work the issue is.', 10),
|
|
16
|
+
(1, 'Priority', 'How soon it should be done.', 20)
|
|
17
|
+
on conflict (workspace_id, lower(name)) do nothing;
|
|
18
|
+
|
|
19
|
+
insert into meta.team_label (workspace_id, group_id, name, color, description, seq)
|
|
20
|
+
select 1, g.id, l.name, l.color, l.description, l.seq
|
|
21
|
+
from (values ('Type', 'Bug', 'red', 'Something does not work as it should.', 10),
|
|
22
|
+
('Type', 'Feature', 'blue', 'New functionality.', 20),
|
|
23
|
+
('Type', 'Task', 'teal', 'Work to do that is neither a bug nor a feature.', 30),
|
|
24
|
+
('Priority', 'High', 'orange', 'Do this first.', 10),
|
|
25
|
+
('Priority', 'Medium', 'yellow', 'Do this soon.', 20),
|
|
26
|
+
('Priority', 'Low', 'gray', 'Do this when there is time.', 30)) as l(grp, name, color, description, seq)
|
|
27
|
+
join meta.team_label_group g on g.workspace_id = 1 and g.name = l.grp
|
|
28
|
+
on conflict (workspace_id, lower(name)) do nothing;
|
|
29
|
+
|
|
30
|
+
insert into meta.team_milestone (workspace_id, name, description, due_date) values
|
|
31
|
+
(1, '1.0', 'The first release.', current_date + 30),
|
|
32
|
+
(1, '1.1', 'The first update after the release.', current_date + 90)
|
|
33
|
+
on conflict (workspace_id, lower(name)) do nothing;
|
|
34
|
+
|
|
35
|
+
insert into meta.team_template (workspace_id, kind, name, title, body, labels, seq) values
|
|
36
|
+
(1, 'issue', 'Bug report', 'Bug: ', E'## What happens\n\n\n## What should happen\n\n\n## Steps to reproduce\n\n1. \n2. \n', '{Bug}', 10),
|
|
37
|
+
(1, 'response', 'Thanks, we will look at it', null, 'Thank you for reporting this. We will look at it and keep this issue up to date.', '{}', 10)
|
|
38
|
+
on conflict (workspace_id, kind, lower(name)) do nothing;
|
|
39
|
+
|
|
40
|
+
with issue as (
|
|
41
|
+
insert into meta.team_issue (workspace_id, title, body, milestone_id, app_id, page_no, created_by)
|
|
42
|
+
select 1, 'Salary rounding on page 12',
|
|
43
|
+
E'The leave calendar shows daily salary costs rounded to whole euros; finance expects cents.\n\n'
|
|
44
|
+
|| E'- Open **page 12** (Leave calendar)\n- Compare a day with the salary in `hr.emp`\n\n@admin can you have a look?',
|
|
45
|
+
(select id from meta.team_milestone where workspace_id = 1 and name = '1.1'),
|
|
46
|
+
a.id, 12, 'admin'
|
|
47
|
+
from meta.app a
|
|
48
|
+
where a.alias = 'hr'
|
|
49
|
+
and not exists (select 1 from meta.team_issue i where i.workspace_id = 1 and i.title = 'Salary rounding on page 12')
|
|
50
|
+
returning id
|
|
51
|
+
), labelled as (
|
|
52
|
+
insert into meta.team_issue_label (issue_id, label_id)
|
|
53
|
+
select issue.id, l.id from issue join meta.team_label l on l.workspace_id = 1 and l.name in ('Bug', 'Medium')
|
|
54
|
+
), assigned as (
|
|
55
|
+
insert into meta.team_issue_assignee (issue_id, username)
|
|
56
|
+
select issue.id, 'admin' from issue where exists (select 1 from meta.developer where username = 'admin')
|
|
57
|
+
)
|
|
58
|
+
insert into meta.team_issue_event (issue_id, username, action) select id, 'admin', 'created' from issue;
|
|
@@ -0,0 +1,113 @@
|
|
|
1
|
+
-- =====================================================================
|
|
2
|
+
-- HR example, part 67: custom task outcomes, task events and
|
|
3
|
+
-- notifications, a Send E-Mail step, drafts and versions of a workflow
|
|
4
|
+
-- (migration 123; docs/guide/06-processing.md, "Approvals" and "Workflows")
|
|
5
|
+
--
|
|
6
|
+
-- SALARY_DECISION is a task with three outcomes instead of Approve / Reject:
|
|
7
|
+
-- Yes, No and Undecided. Its owners (role admin) get an e-mail when a task
|
|
8
|
+
-- is created (notification SALARY_TASK); when one is completed an event
|
|
9
|
+
-- records the decision as a notification for the initiator.
|
|
10
|
+
--
|
|
11
|
+
-- SALARY_CHANGE asks for that decision and branches on its outcome:
|
|
12
|
+
-- 1. APPROVE the task (an administrator decides)
|
|
13
|
+
-- 2. DECIDE YES: apply the new salary; UNDECIDED: ask again; otherwise the end
|
|
14
|
+
-- 3. APPLY the new salary (SQL)
|
|
15
|
+
-- 4. TELL an e-mail to the employee (version 2: a send_mail step)
|
|
16
|
+
-- Version 1 had no TELL step and is inactive; version 2 is active; version 3
|
|
17
|
+
-- is in development with a draft step (BUDGET_CHECK) that is still a
|
|
18
|
+
-- placeholder: it is drawn dashed, and can't be activated until it is
|
|
19
|
+
-- replaced. Instances of version 1 can be migrated to version 2 under
|
|
20
|
+
-- Shared Components → Workflows → SALARY_CHANGE → Migrate running instances.
|
|
21
|
+
--
|
|
22
|
+
-- Start one from SQL (e.g. the SQL Workshop, signed in to the application):
|
|
23
|
+
-- select meta.start_workflow('SALARY_CHANGE', '7499', '{"ENAME": "Allen", "NEW_SAL": 1760}')
|
|
24
|
+
-- and decide on it under My tasks (page 14) as king.
|
|
25
|
+
-- =====================================================================
|
|
26
|
+
|
|
27
|
+
insert into meta.email_template (app_id, name, subject, html_body, text_body)
|
|
28
|
+
select id, 'SALARY_TASK', 'To decide: #SUBJECT#',
|
|
29
|
+
$h$<p>A salary change waits for your decision: <b>#SUBJECT#</b>.</p>
|
|
30
|
+
<p>Requested by #INITIATOR#. Decide under <i>My tasks</i> in HR Demo.</p>$h$,
|
|
31
|
+
$t$A salary change waits for your decision: #SUBJECT#.
|
|
32
|
+
|
|
33
|
+
Requested by #INITIATOR#. Decide under "My tasks" in HR Demo.$t$
|
|
34
|
+
from meta.app where alias = 'hr'
|
|
35
|
+
union all
|
|
36
|
+
select id, 'SALARY_CHANGED', 'Your salary changes to #NEW_SAL#',
|
|
37
|
+
$h$<p>Dear #ENAME#,</p><p>Your new salary of <b>#NEW_SAL#</b> was approved by #TASK_APPROVER#.</p><p>Kind regards,<br>HR</p>$h$,
|
|
38
|
+
$t$Dear #ENAME#,
|
|
39
|
+
|
|
40
|
+
Your new salary of #NEW_SAL# was approved by #TASK_APPROVER#.
|
|
41
|
+
|
|
42
|
+
Kind regards,
|
|
43
|
+
HR$t$
|
|
44
|
+
from meta.app where alias = 'hr';
|
|
45
|
+
|
|
46
|
+
insert into meta.task_definition (app_id, name, type, subject, owner_roles, admin_role, priority, due_in, outcomes, events, notifications)
|
|
47
|
+
select id, 'SALARY_DECISION', 'approval', 'Salary of &ENAME. to &NEW_SAL.', '{admin}'::text[], 'admin', 2, '5 days',
|
|
48
|
+
$o$[{"name": "YES", "label": "Yes", "colour": "success"},
|
|
49
|
+
{"name": "NO", "label": "No", "colour": "danger"},
|
|
50
|
+
{"name": "UNDECIDED", "label": "Undecided", "colour": "warning"}]$o$::jsonb,
|
|
51
|
+
$e$[{"on": "complete", "type": "sql", "code": "select hr.salary_decided(:DETAIL_PK::int, :OUTCOME, :INITIATOR)"}]$e$::jsonb,
|
|
52
|
+
'{"create": "SALARY_TASK"}'::jsonb
|
|
53
|
+
from meta.app where alias = 'hr';
|
|
54
|
+
|
|
55
|
+
-- Notifications are for other users, which RLS forbids the app: security definer, like the onboarding ones.
|
|
56
|
+
create function hr.salary_decided(p_empno int, p_outcome text, p_initiator text) returns void
|
|
57
|
+
language plpgsql security definer set search_path = hr, pg_catalog as $$
|
|
58
|
+
begin
|
|
59
|
+
insert into hr.notification (username, message)
|
|
60
|
+
select p_initiator, format('Salary change of %s: %s.', initcap(e.ename), lower(p_outcome))
|
|
61
|
+
from hr.emp e where e.empno = p_empno;
|
|
62
|
+
end
|
|
63
|
+
$$;
|
|
64
|
+
create function hr.salary_change_apply(p_empno int, p_sal numeric) returns timestamptz
|
|
65
|
+
language plpgsql security definer set search_path = hr, pg_catalog as $$
|
|
66
|
+
begin
|
|
67
|
+
if p_sal is null or p_sal <= 0 then
|
|
68
|
+
raise exception 'A salary is more than 0.';
|
|
69
|
+
end if;
|
|
70
|
+
update hr.emp set sal = p_sal where empno = p_empno;
|
|
71
|
+
return now();
|
|
72
|
+
end
|
|
73
|
+
$$;
|
|
74
|
+
-- the e-mail address of an employee's account (the accounts are pgkiln's, not the application's)
|
|
75
|
+
create function hr.employee_email(p_empno int) returns text
|
|
76
|
+
language sql stable security definer set search_path = hr, pg_catalog as $$
|
|
77
|
+
select a.email from hr.emp e join meta.account a on lower(a.username) = lower(e.username) where e.empno = p_empno and a.active
|
|
78
|
+
$$;
|
|
79
|
+
revoke all on function hr.salary_decided(int, text, text), hr.salary_change_apply(int, numeric), hr.employee_email(int) from public;
|
|
80
|
+
grant execute on function hr.salary_decided(int, text, text), hr.salary_change_apply(int, numeric), hr.employee_email(int) to hr_app;
|
|
81
|
+
|
|
82
|
+
-- version 1, then version 2 (with the e-mail) becomes active, then version 3 in development (a draft step)
|
|
83
|
+
insert into meta.workflow_definition (app_id, name, title, description, admin_role, steps)
|
|
84
|
+
select id, 'SALARY_CHANGE', 'Salary of &ENAME. to &NEW_SAL.', 'A salary change, decided by an administrator (Yes, No or Undecided).', 'admin',
|
|
85
|
+
$s$[
|
|
86
|
+
{"name": "APPROVE", "type": "task", "task": "SALARY_DECISION"},
|
|
87
|
+
{"name": "DECIDE", "type": "switch", "cases": [{"outcome": "YES", "next": "APPLY"}, {"outcome": "UNDECIDED", "next": "APPROVE"}], "otherwise": "END"},
|
|
88
|
+
{"name": "APPLY", "type": "sql", "code": "select hr.salary_change_apply(:DETAIL_PK::int, :NEW_SAL::numeric) as applied_at", "next": "END"},
|
|
89
|
+
{"name": "END", "type": "end"}
|
|
90
|
+
]$s$::jsonb
|
|
91
|
+
from meta.app where alias = 'hr';
|
|
92
|
+
|
|
93
|
+
select meta.new_workflow_version(d.id) from meta.workflow_definition d join meta.app a on a.id = d.app_id where a.alias = 'hr' and d.name = 'SALARY_CHANGE';
|
|
94
|
+
update meta.workflow_definition d set dev_steps = $s$[
|
|
95
|
+
{"name": "APPROVE", "type": "task", "task": "SALARY_DECISION"},
|
|
96
|
+
{"name": "DECIDE", "type": "switch", "cases": [{"outcome": "YES", "next": "APPLY"}, {"outcome": "UNDECIDED", "next": "APPROVE"}], "otherwise": "END"},
|
|
97
|
+
{"name": "APPLY", "type": "sql", "code": "select hr.salary_change_apply(:DETAIL_PK::int, :NEW_SAL::numeric) as applied_at"},
|
|
98
|
+
{"name": "TELL", "type": "send_mail", "to": "select hr.employee_email(:DETAIL_PK::int)", "template": "SALARY_CHANGED", "next": "END"},
|
|
99
|
+
{"name": "END", "type": "end"}
|
|
100
|
+
]$s$::jsonb
|
|
101
|
+
from meta.app a where a.id = d.app_id and a.alias = 'hr' and d.name = 'SALARY_CHANGE';
|
|
102
|
+
select meta.activate_workflow_version(d.id) from meta.workflow_definition d join meta.app a on a.id = d.app_id where a.alias = 'hr' and d.name = 'SALARY_CHANGE';
|
|
103
|
+
|
|
104
|
+
select meta.new_workflow_version(d.id) from meta.workflow_definition d join meta.app a on a.id = d.app_id where a.alias = 'hr' and d.name = 'SALARY_CHANGE';
|
|
105
|
+
update meta.workflow_definition d set dev_steps = $s$[
|
|
106
|
+
{"name": "BUDGET_CHECK", "type": "draft", "note": "check the department's budget", "next": "APPROVE"},
|
|
107
|
+
{"name": "APPROVE", "type": "task", "task": "SALARY_DECISION"},
|
|
108
|
+
{"name": "DECIDE", "type": "switch", "cases": [{"outcome": "YES", "next": "APPLY"}, {"outcome": "UNDECIDED", "next": "APPROVE"}], "otherwise": "END"},
|
|
109
|
+
{"name": "APPLY", "type": "sql", "code": "select hr.salary_change_apply(:DETAIL_PK::int, :NEW_SAL::numeric) as applied_at"},
|
|
110
|
+
{"name": "TELL", "type": "send_mail", "to": "select hr.employee_email(:DETAIL_PK::int)", "template": "SALARY_CHANGED", "next": "END"},
|
|
111
|
+
{"name": "END", "type": "end"}
|
|
112
|
+
]$s$::jsonb
|
|
113
|
+
from meta.app a where a.id = d.app_id and a.alias = 'hr' and d.name = 'SALARY_CHANGE';
|