pgkiln 0.42.0 → 0.43.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (52) hide show
  1. package/db/migrations/099_layout_positions.sql +29 -0
  2. package/docs/apex-feature-parity.md +34 -7
  3. package/docs/guide/03-builder.md +67 -13
  4. package/docs/guide/04-pages-and-regions.md +46 -3
  5. package/docs/guide/05-items.md +1 -1
  6. package/docs/guide/09-reference.md +4 -1
  7. package/docs/guide/12-development.md +2 -1
  8. package/package.json +1 -1
  9. package/public/app.css +58 -0
  10. package/public/app.js +183 -0
  11. package/public/builder.css +112 -0
  12. package/public/builder.js +280 -3
  13. package/src/builder/arrange.ts +133 -17
  14. package/src/builder/components.ts +28 -22
  15. package/src/builder/designer.ts +67 -18
  16. package/src/builder/forms.ts +4 -1
  17. package/src/builder/object-browser.ts +342 -0
  18. package/src/builder/pwa.ts +3 -3
  19. package/src/builder/routes.ts +85 -26
  20. package/src/builder/shared.ts +103 -1
  21. package/src/builder/sql.ts +13 -65
  22. package/src/builder/ui.ts +21 -6
  23. package/src/i18n/ar.ts +16 -0
  24. package/src/i18n/cs.ts +16 -0
  25. package/src/i18n/da.ts +16 -0
  26. package/src/i18n/de.ts +16 -0
  27. package/src/i18n/el.ts +16 -0
  28. package/src/i18n/es.ts +16 -0
  29. package/src/i18n/fi.ts +16 -0
  30. package/src/i18n/fr.ts +16 -0
  31. package/src/i18n/he.ts +16 -0
  32. package/src/i18n/it.ts +16 -0
  33. package/src/i18n/ja.ts +16 -0
  34. package/src/i18n/ko.ts +16 -0
  35. package/src/i18n/nb.ts +16 -0
  36. package/src/i18n/pl.ts +16 -0
  37. package/src/i18n/pt.ts +16 -0
  38. package/src/i18n/ru.ts +16 -0
  39. package/src/i18n/sv.ts +16 -0
  40. package/src/i18n/tr.ts +16 -0
  41. package/src/i18n/uk.ts +16 -0
  42. package/src/i18n/zh.ts +16 -0
  43. package/src/i18n.ts +32 -0
  44. package/src/metadata.ts +5 -1
  45. package/src/runtime/engine.ts +6 -0
  46. package/src/runtime/format.ts +52 -0
  47. package/src/runtime/items.ts +27 -0
  48. package/src/runtime/locale.ts +3 -0
  49. package/src/runtime/regions.ts +36 -2
  50. package/src/runtime/render.ts +20 -4
  51. package/src/runtime/report.ts +134 -79
  52. package/src/runtime/routes.ts +34 -3
@@ -24,15 +24,16 @@ import { startDebug } from '../debug.ts';
24
24
  import { assignable, clearPageItems, fetchForms, processConditionHolds, ProcessFailed, runAppProcesses, runProcesses, runSql, validate, ValidationFailed } from './engine.ts';
25
25
  import { aiInputs, aiProcessOf, runAiProcess } from './ai.ts';
26
26
  import { branchTarget, ComputationFailed, runComputations } from './logic.ts';
27
- import { comboMultiple, itemMask, MULTI_VALUE, popupPageSize, renderItem, searchLov, splitValues } from './items.ts';
27
+ import { comboMultiple, itemDateMask, itemMask, MULTI_VALUE, popupPageSize, renderItem, searchLov, splitValues } from './items.ts';
28
28
  import { parseNumber } from '../numformat.ts';
29
+ import { parseDateMask } from './format.ts';
29
30
  import { cleanRichText } from '../richtext.ts';
30
31
  import { applyUploads, fileRoutes, readMultipart, type Upload } from './files.ts';
31
32
  import { renderRegion } from './regions.ts';
32
33
  import { downloadHeaders, type Download } from './processes.ts';
33
34
  import { moveGanttTask } from './gantt-move.ts';
34
35
  import { moveCalendarEvent } from './calendar.ts';
35
- import { openDownload, reportParams, normaliseReportParams, selectionOf } from './report.ts';
36
+ import { columnValues, openDownload, reportParams, normaliseReportParams, selectionOf } from './report.ts';
36
37
  import { invalidatePage, lazyOf } from './region-cache.ts';
37
38
  import { applyRowSelection, mastersOf } from './master-detail.ts';
38
39
  import { layerInArea, layerTile, parseTile, servedLayer } from './maps.ts';
@@ -373,7 +374,10 @@ function applyPostedItems(ctx: PageContext, body: Body, only?: string[]) {
373
374
  else if (item.type === 'markdown') ctx.session.state[item.name] = posted ? String(posted).replace(/\r\n?/g, '\n') : null;
374
375
  else if (item.type === 'checkbox' || item.type === 'switch') ctx.session.state[item.name] = posted === 'true' ? 'true' : 'false';
375
376
  else if (item.type === 'password' && !posted) continue;
376
- else if (itemMask(item) && posted !== undefined && posted !== '') {
377
+ else if (item.type === 'date' && itemDateMask(ctx, item) && posted !== undefined && posted !== '') {
378
+ // (sprint 76) "23-05-1987" in the date format → "1987-05-23"; other text is kept as typed, and validate() reports it
379
+ ctx.session.state[item.name] = parseDateMask(String(posted), itemDateMask(ctx, item)!, ctx.locale.lang) ?? String(posted);
380
+ } else if (itemMask(item) && posted !== undefined && posted !== '') {
377
381
  // "1.234,50 €" → "1234.5"; text that isn't a number is kept as typed, and validate() reports it
378
382
  const parsed = parseNumber(String(posted), itemMask(item), ctx.locale.numbers);
379
383
  ctx.session.state[item.name] = parsed === null ? String(posted) : parsed || null;
@@ -736,6 +740,33 @@ export async function runtimeRoutes(app: FastifyInstance) {
736
740
  }
737
741
  });
738
742
 
743
+ // ---------------------------------------------------------------- a column heading's values
744
+ // (sprint 76) The values of a report column with their counts, for its heading's menu: the report must be
745
+ // visible and interactive; the query runs as the application's role with the request's search and filters;
746
+ // only a column the user may see. Read only.
747
+ app.get('/a/:alias/:page/report/:id/values', async (req: Req, reply) => {
748
+ const ctx = await loadContext(req, reply, { json: true });
749
+ if (!ctx) return;
750
+ reply.header('cache-control', 'private, no-store');
751
+ const column = String((req.query as Record<string, unknown>)?.col ?? '');
752
+ try {
753
+ const out = await appTx(txContext(ctx), async (c) => {
754
+ ctx.client = c;
755
+ await checkPageAccess(ctx);
756
+ const vis = await computeVisibility(ctx);
757
+ const r = ctx.page.regions.find((x) => x.id === Number(req.params.id) && x.type === 'report');
758
+ if (!r || !vis.regions.has(r.id) || r.config.interactive === false || r.config.searchable === false) throw new Forbidden(ctx.locale.t('error.access_denied'));
759
+ const list = column ? await columnValues(ctx, r, column) : null;
760
+ if (!list) throw new Forbidden(ctx.locale.t('error.access_denied'));
761
+ return { values: list };
762
+ });
763
+ return reply.send(out);
764
+ } catch (e) {
765
+ if (e instanceof Forbidden) return reply.code(403).send({ error: e.message });
766
+ return reply.code(400).send({ error: await publicError(ctx, e, 'column values') });
767
+ }
768
+ });
769
+
739
770
  // ---------------------------------------------------------------- map layers loaded by the browser
740
771
  // (0.31) A map layer with "visible_area" (JSON for ?bb=south,west,north,east)
741
772
  // or "tiles" (Mapbox Vector Tiles, …/tiles/<layer>/<z>/<x>/<y>.mvt). Page