@fieldia/core 0.28.0 → 0.30.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 (251) hide show
  1. package/README.md +125 -1
  2. package/package.json +1 -1
  3. package/schema/page.schema.json +279 -6
  4. package/src/index.d.mts +8 -2
  5. package/src/index.d.ts +8 -2
  6. package/src/index.js +29 -2
  7. package/src/index.js.map +1 -1
  8. package/src/index.mjs +11 -2
  9. package/src/index.mjs.map +1 -1
  10. package/src/lib/format/approval-checks.d.mts +8 -0
  11. package/src/lib/format/approval-checks.d.ts +8 -0
  12. package/src/lib/format/approval-checks.js +29 -0
  13. package/src/lib/format/approval-checks.js.map +1 -0
  14. package/src/lib/format/approval-checks.mjs +26 -0
  15. package/src/lib/format/approval-checks.mjs.map +1 -0
  16. package/src/lib/format/approval.d.mts +20 -0
  17. package/src/lib/format/approval.d.ts +20 -0
  18. package/src/lib/format/approval.js +25 -0
  19. package/src/lib/format/approval.js.map +1 -0
  20. package/src/lib/format/approval.mjs +22 -0
  21. package/src/lib/format/approval.mjs.map +1 -0
  22. package/src/lib/format/assist-checks.d.mts +14 -0
  23. package/src/lib/format/assist-checks.d.ts +14 -0
  24. package/src/lib/format/assist-checks.js +68 -0
  25. package/src/lib/format/assist-checks.js.map +1 -0
  26. package/src/lib/format/assist-checks.mjs +64 -0
  27. package/src/lib/format/assist-checks.mjs.map +1 -0
  28. package/src/lib/format/assist.d.mts +41 -0
  29. package/src/lib/format/assist.d.ts +41 -0
  30. package/src/lib/format/assist.js +29 -0
  31. package/src/lib/format/assist.js.map +1 -0
  32. package/src/lib/format/assist.mjs +26 -0
  33. package/src/lib/format/assist.mjs.map +1 -0
  34. package/src/lib/format/editable-checks.d.mts +8 -0
  35. package/src/lib/format/editable-checks.d.ts +8 -0
  36. package/src/lib/format/editable-checks.js +38 -0
  37. package/src/lib/format/editable-checks.js.map +1 -0
  38. package/src/lib/format/editable-checks.mjs +35 -0
  39. package/src/lib/format/editable-checks.mjs.map +1 -0
  40. package/src/lib/format/field.d.mts +86 -0
  41. package/src/lib/format/field.d.ts +86 -0
  42. package/src/lib/format/field.js +56 -10
  43. package/src/lib/format/field.js.map +1 -1
  44. package/src/lib/format/field.mjs +55 -9
  45. package/src/lib/format/field.mjs.map +1 -1
  46. package/src/lib/format/head-checks.d.mts +10 -2
  47. package/src/lib/format/head-checks.d.ts +10 -2
  48. package/src/lib/format/head-checks.js +9 -2
  49. package/src/lib/format/head-checks.js.map +1 -1
  50. package/src/lib/format/head-checks.mjs +10 -4
  51. package/src/lib/format/head-checks.mjs.map +1 -1
  52. package/src/lib/format/languages-checks.d.mts +8 -0
  53. package/src/lib/format/languages-checks.d.ts +8 -0
  54. package/src/lib/format/languages-checks.js +40 -0
  55. package/src/lib/format/languages-checks.js.map +1 -0
  56. package/src/lib/format/languages-checks.mjs +37 -0
  57. package/src/lib/format/languages-checks.mjs.map +1 -0
  58. package/src/lib/format/later-checks.d.mts +4 -3
  59. package/src/lib/format/later-checks.d.ts +4 -3
  60. package/src/lib/format/later-checks.js +4 -3
  61. package/src/lib/format/later-checks.js.map +1 -1
  62. package/src/lib/format/later-checks.mjs +4 -3
  63. package/src/lib/format/later-checks.mjs.map +1 -1
  64. package/src/lib/format/layout.d.mts +129 -26
  65. package/src/lib/format/layout.d.ts +129 -26
  66. package/src/lib/format/layout.js +24 -3
  67. package/src/lib/format/layout.js.map +1 -1
  68. package/src/lib/format/layout.mjs +22 -1
  69. package/src/lib/format/layout.mjs.map +1 -1
  70. package/src/lib/format/layout.types.d.mts +2 -1
  71. package/src/lib/format/layout.types.d.ts +2 -1
  72. package/src/lib/format/list-editable.d.mts +20 -0
  73. package/src/lib/format/list-editable.d.ts +20 -0
  74. package/src/lib/format/list-editable.js +33 -0
  75. package/src/lib/format/list-editable.js.map +1 -0
  76. package/src/lib/format/list-editable.mjs +28 -0
  77. package/src/lib/format/list-editable.mjs.map +1 -0
  78. package/src/lib/format/page.d.mts +88 -9
  79. package/src/lib/format/page.d.ts +88 -9
  80. package/src/lib/format/page.js +4 -0
  81. package/src/lib/format/page.js.map +1 -1
  82. package/src/lib/format/page.mjs +4 -0
  83. package/src/lib/format/page.mjs.map +1 -1
  84. package/src/lib/format/photos-options.d.mts +29 -0
  85. package/src/lib/format/photos-options.d.ts +29 -0
  86. package/src/lib/format/photos-options.js +49 -0
  87. package/src/lib/format/photos-options.js.map +1 -0
  88. package/src/lib/format/photos-options.mjs +45 -0
  89. package/src/lib/format/photos-options.mjs.map +1 -0
  90. package/src/lib/format/references.d.mts +3 -1
  91. package/src/lib/format/references.d.ts +3 -1
  92. package/src/lib/format/references.js +41 -14
  93. package/src/lib/format/references.js.map +1 -1
  94. package/src/lib/format/references.mjs +42 -15
  95. package/src/lib/format/references.mjs.map +1 -1
  96. package/src/lib/format/scan-checks.d.mts +12 -0
  97. package/src/lib/format/scan-checks.d.ts +12 -0
  98. package/src/lib/format/scan-checks.js +89 -0
  99. package/src/lib/format/scan-checks.js.map +1 -0
  100. package/src/lib/format/scan-checks.mjs +86 -0
  101. package/src/lib/format/scan-checks.mjs.map +1 -0
  102. package/src/lib/format/scan-options.d.mts +84 -0
  103. package/src/lib/format/scan-options.d.ts +84 -0
  104. package/src/lib/format/scan-options.js +58 -0
  105. package/src/lib/format/scan-options.js.map +1 -0
  106. package/src/lib/format/scan-options.mjs +54 -0
  107. package/src/lib/format/scan-options.mjs.map +1 -0
  108. package/src/lib/format/sheet-head-checks.d.mts +10 -2
  109. package/src/lib/format/sheet-head-checks.d.ts +10 -2
  110. package/src/lib/format/sheet-head-checks.js +24 -0
  111. package/src/lib/format/sheet-head-checks.js.map +1 -1
  112. package/src/lib/format/sheet-head-checks.mjs +23 -0
  113. package/src/lib/format/sheet-head-checks.mjs.map +1 -1
  114. package/src/lib/format/upgrade.d.mts +6 -2
  115. package/src/lib/format/upgrade.d.ts +6 -2
  116. package/src/lib/format/upgrade.js +6 -2
  117. package/src/lib/format/upgrade.js.map +1 -1
  118. package/src/lib/format/upgrade.mjs +6 -2
  119. package/src/lib/format/upgrade.mjs.map +1 -1
  120. package/src/lib/format/version.d.mts +2 -2
  121. package/src/lib/format/version.d.ts +2 -2
  122. package/src/lib/format/version.js +2 -2
  123. package/src/lib/format/version.js.map +1 -1
  124. package/src/lib/format/version.mjs +2 -2
  125. package/src/lib/format/version.mjs.map +1 -1
  126. package/src/lib/record/approvals-part.d.mts +6 -0
  127. package/src/lib/record/approvals-part.d.ts +6 -0
  128. package/src/lib/record/approvals-part.js +14 -0
  129. package/src/lib/record/approvals-part.js.map +1 -0
  130. package/src/lib/record/approvals-part.mjs +7 -0
  131. package/src/lib/record/approvals-part.mjs.map +1 -0
  132. package/src/lib/record/approving.d.mts +45 -0
  133. package/src/lib/record/approving.d.ts +45 -0
  134. package/src/lib/record/approving.js +10 -0
  135. package/src/lib/record/approving.js.map +1 -0
  136. package/src/lib/record/approving.mjs +7 -0
  137. package/src/lib/record/approving.mjs.map +1 -0
  138. package/src/lib/record/check.js +4 -0
  139. package/src/lib/record/check.js.map +1 -1
  140. package/src/lib/record/check.mjs +4 -0
  141. package/src/lib/record/check.mjs.map +1 -1
  142. package/src/lib/record/data-source.d.mts +270 -0
  143. package/src/lib/record/data-source.d.ts +270 -0
  144. package/src/lib/record/data-source.js.map +1 -1
  145. package/src/lib/record/data-source.mjs.map +1 -1
  146. package/src/lib/record/files-part.d.mts +7 -0
  147. package/src/lib/record/files-part.d.ts +7 -0
  148. package/src/lib/record/files-part.js +167 -0
  149. package/src/lib/record/files-part.js.map +1 -0
  150. package/src/lib/record/files-part.mjs +163 -0
  151. package/src/lib/record/files-part.mjs.map +1 -0
  152. package/src/lib/record/filter.d.mts +5 -0
  153. package/src/lib/record/filter.d.ts +5 -0
  154. package/src/lib/record/filter.js +1 -0
  155. package/src/lib/record/filter.js.map +1 -1
  156. package/src/lib/record/filter.mjs +1 -1
  157. package/src/lib/record/filter.mjs.map +1 -1
  158. package/src/lib/record/form-approvals.d.mts +47 -0
  159. package/src/lib/record/form-approvals.d.ts +47 -0
  160. package/src/lib/record/form-approvals.js +186 -0
  161. package/src/lib/record/form-approvals.js.map +1 -0
  162. package/src/lib/record/form-approvals.mjs +180 -0
  163. package/src/lib/record/form-approvals.mjs.map +1 -0
  164. package/src/lib/record/form.d.mts +32 -1
  165. package/src/lib/record/form.d.ts +32 -1
  166. package/src/lib/record/form.js +60 -15
  167. package/src/lib/record/form.js.map +1 -1
  168. package/src/lib/record/form.mjs +60 -15
  169. package/src/lib/record/form.mjs.map +1 -1
  170. package/src/lib/record/languages-part.d.mts +32 -0
  171. package/src/lib/record/languages-part.d.ts +32 -0
  172. package/src/lib/record/languages-part.js +104 -0
  173. package/src/lib/record/languages-part.js.map +1 -0
  174. package/src/lib/record/languages-part.mjs +96 -0
  175. package/src/lib/record/languages-part.mjs.map +1 -0
  176. package/src/lib/record/languages.d.mts +26 -0
  177. package/src/lib/record/languages.d.ts +26 -0
  178. package/src/lib/record/languages.js +20 -0
  179. package/src/lib/record/languages.js.map +1 -0
  180. package/src/lib/record/languages.mjs +15 -0
  181. package/src/lib/record/languages.mjs.map +1 -0
  182. package/src/lib/record/locales/ar.js +5 -0
  183. package/src/lib/record/locales/ar.js.map +1 -1
  184. package/src/lib/record/locales/ar.mjs +5 -0
  185. package/src/lib/record/locales/ar.mjs.map +1 -1
  186. package/src/lib/record/locales/de.js +5 -0
  187. package/src/lib/record/locales/de.js.map +1 -1
  188. package/src/lib/record/locales/de.mjs +5 -0
  189. package/src/lib/record/locales/de.mjs.map +1 -1
  190. package/src/lib/record/locales/fr.js +5 -0
  191. package/src/lib/record/locales/fr.js.map +1 -1
  192. package/src/lib/record/locales/fr.mjs +5 -0
  193. package/src/lib/record/locales/fr.mjs.map +1 -1
  194. package/src/lib/record/memory-approvals.d.mts +47 -0
  195. package/src/lib/record/memory-approvals.d.ts +47 -0
  196. package/src/lib/record/memory-approvals.js +78 -0
  197. package/src/lib/record/memory-approvals.js.map +1 -0
  198. package/src/lib/record/memory-approvals.mjs +75 -0
  199. package/src/lib/record/memory-approvals.mjs.map +1 -0
  200. package/src/lib/record/memory-assist.d.mts +53 -0
  201. package/src/lib/record/memory-assist.d.ts +53 -0
  202. package/src/lib/record/memory-assist.js +519 -0
  203. package/src/lib/record/memory-assist.js.map +1 -0
  204. package/src/lib/record/memory-assist.mjs +513 -0
  205. package/src/lib/record/memory-assist.mjs.map +1 -0
  206. package/src/lib/record/memory-clusters.d.mts +18 -0
  207. package/src/lib/record/memory-clusters.d.ts +18 -0
  208. package/src/lib/record/memory-clusters.js +83 -0
  209. package/src/lib/record/memory-clusters.js.map +1 -0
  210. package/src/lib/record/memory-clusters.mjs +80 -0
  211. package/src/lib/record/memory-clusters.mjs.map +1 -0
  212. package/src/lib/record/memory-data-source.d.mts +43 -3
  213. package/src/lib/record/memory-data-source.d.ts +43 -3
  214. package/src/lib/record/memory-data-source.js +105 -19
  215. package/src/lib/record/memory-data-source.js.map +1 -1
  216. package/src/lib/record/memory-data-source.mjs +105 -19
  217. package/src/lib/record/memory-data-source.mjs.map +1 -1
  218. package/src/lib/record/memory-later.d.mts +1 -1
  219. package/src/lib/record/memory-later.d.ts +1 -1
  220. package/src/lib/record/memory-later.js +4 -1
  221. package/src/lib/record/memory-later.js.map +1 -1
  222. package/src/lib/record/memory-later.mjs +4 -1
  223. package/src/lib/record/memory-later.mjs.map +1 -1
  224. package/src/lib/record/messages.d.mts +14 -0
  225. package/src/lib/record/messages.d.ts +14 -0
  226. package/src/lib/record/messages.js +5 -0
  227. package/src/lib/record/messages.js.map +1 -1
  228. package/src/lib/record/messages.mjs +5 -0
  229. package/src/lib/record/messages.mjs.map +1 -1
  230. package/src/lib/record/run.d.mts +6 -1
  231. package/src/lib/record/run.d.ts +6 -1
  232. package/src/lib/record/run.js.map +1 -1
  233. package/src/lib/record/run.mjs.map +1 -1
  234. package/src/lib/record/uploads.d.mts +35 -9
  235. package/src/lib/record/uploads.d.ts +35 -9
  236. package/src/lib/record/uploads.js +49 -169
  237. package/src/lib/record/uploads.js.map +1 -1
  238. package/src/lib/record/uploads.mjs +42 -164
  239. package/src/lib/record/uploads.mjs.map +1 -1
  240. package/src/lib/record/values.d.mts +23 -0
  241. package/src/lib/record/values.d.ts +23 -0
  242. package/src/lib/record/values.js +4 -0
  243. package/src/lib/record/values.js.map +1 -1
  244. package/src/lib/record/values.mjs +4 -0
  245. package/src/lib/record/values.mjs.map +1 -1
  246. package/src/runtime.d.mts +12 -5
  247. package/src/runtime.d.ts +12 -5
  248. package/src/runtime.js +18 -3
  249. package/src/runtime.js.map +1 -1
  250. package/src/runtime.mjs +7 -1
  251. package/src/runtime.mjs.map +1 -1
@@ -0,0 +1,180 @@
1
+ import { saveProblemOf } from './data-source.mjs';
2
+ import { fill } from './messages.mjs';
3
+ import { dayWords } from './day-words.mjs';
4
+ import { isOwnZone, wallClock, wallDay, wallTime } from './zones.mjs';
5
+ import { uploadGoing } from './uploads.mjs';
6
+ const STATES = ['waiting', 'later', 'approved', 'rejected', 'skipped'];
7
+ const said = (value) => typeof value === 'string' && value.trim() !== '';
8
+ /** A level as the source told it, kept to what can be said: its id, its name, its approvers by name, a state Fieldia knows. */
9
+ function levelOf(told) {
10
+ const level = told;
11
+ if (!level || !said(level.id) || !STATES.includes(level.state))
12
+ return null;
13
+ const approvers = (Array.isArray(level.approvers) ? level.approvers : []).filter((one) => !!one && said(one.name));
14
+ return { id: level.id, name: said(level.name) ? level.name : level.id, approvers, needs: level.needs === 'all' ? 'all' : 'any', state: level.state };
15
+ }
16
+ /** Where a record stands as a whole, from its levels: a rejection ends it, a level waiting is waited for. */
17
+ export function approvalStatus(levels) {
18
+ const asked = levels.filter((level) => level.state !== 'skipped');
19
+ if (!asked.length)
20
+ return 'none';
21
+ if (asked.some((level) => level.state === 'rejected'))
22
+ return 'rejected';
23
+ if (asked.some((level) => level.state === 'waiting'))
24
+ return 'waiting';
25
+ if (asked.every((level) => level.state === 'approved'))
26
+ return 'approved';
27
+ return 'needed';
28
+ }
29
+ /** What the form keeps of a source's answer: the levels it can say, those this person may decide now — where the source takes decisions. */
30
+ export function approvalOf(told, decides) {
31
+ const levels = (Array.isArray(told?.levels) ? told.levels : []).map(levelOf).filter((level) => level !== null);
32
+ const history = (Array.isArray(told?.history) ? told.history : []).filter((entry) => !!entry && said(entry.by) && said(entry.at) && ['approved', 'rejected', 'requested'].includes(entry.decision));
33
+ const waiting = new Set(levels.filter((level) => level.state === 'waiting').map((level) => level.id));
34
+ const mine = decides ? [...new Set((Array.isArray(told?.mine) ? told.mine : []).filter((id) => waiting.has(id)))] : [];
35
+ return {
36
+ state: 'told',
37
+ status: approvalStatus(levels),
38
+ levels,
39
+ together: told?.together === true,
40
+ history,
41
+ mine,
42
+ ...(said(told?.why) ? { why: told.why } : {}),
43
+ deciding: null,
44
+ };
45
+ }
46
+ /** Nothing told yet, for the record shown: asked. */
47
+ const asking = { state: 'asking', status: 'none', levels: [], together: false, history: [], mine: [], deciding: null };
48
+ /**
49
+ * A moment of a record's approval history in words, as the page's date boxes write a day — in its order and language —
50
+ * on its clock, the time after it: "18 Oct 2026, 10:42", with the zone's short name where it is not the browser's own.
51
+ */
52
+ export function approvalWhen(at, messages, page) {
53
+ const ms = Date.parse(at);
54
+ if (Number.isNaN(ms))
55
+ return at;
56
+ const own = page.look?.dateOrder;
57
+ const order = own === 'dmy' || own === 'mdy' || own === 'ymd' ? own : messages.dateOrder;
58
+ const zone = page.timeZone;
59
+ const wall = wallTime(ms, zone);
60
+ const words = `${dayWords(wallDay(wall), messages, order)}${order === 'ymd' ? ' ' : ', '}${wallClock(wall)}`;
61
+ if (!zone || isOwnZone(zone))
62
+ return words;
63
+ const name = new Intl.DateTimeFormat(messages.locale === 'en' ? 'en-GB' : messages.locale, { timeZone: zone, timeZoneName: 'short' }).formatToParts(ms).find((part) => part.type === 'timeZoneName')?.value;
64
+ return name ? `${words} ${name}` : words;
65
+ }
66
+ /** A record's approvals: what the form keeps of them, and what it asks, decides and locks by them. */
67
+ export function formApprovals(host) {
68
+ const { form, messages } = host;
69
+ const page = form.page;
70
+ /** Counted at each ask: only the latest answer is kept, a save's and a load's asks crossing. */
71
+ let asks = 0;
72
+ /** A decision on its way, from the save before it to the record loaded after: a second press is the same press. */
73
+ let deciding = false;
74
+ const user = () => {
75
+ const person = form.user();
76
+ return person ? { user: { id: person.id, ...(person.name ? { name: person.name } : {}) } } : {};
77
+ };
78
+ const state = () => form.getState();
79
+ const model = page.data.kind === 'record' ? page.data.model : '';
80
+ const nameOf = (level) => state().approval?.levels.find((one) => one.id === level)?.name ?? level;
81
+ /** What is typed read and the person's changes saved, before a decision: null once done, or why not, in words. */
82
+ async function saveFirst() {
83
+ await form.settle();
84
+ const now = state();
85
+ if (!now.dirty.length && !Object.keys(now.unread).length && !(now.uploads ?? []).some(uploadGoing))
86
+ return null;
87
+ if (await form.save())
88
+ return null;
89
+ const after = state();
90
+ return after.saveProblem?.message ?? after.error ?? Object.values(after.errors)[0] ?? '';
91
+ }
92
+ async function ask() {
93
+ const source = host.dataSource;
94
+ const recordId = state().recordId;
95
+ if (!source?.approvals || recordId == null || !model) {
96
+ if (state().approval)
97
+ host.set({ approval: null });
98
+ return;
99
+ }
100
+ const shown = form.recordShown();
101
+ const seq = ++asks;
102
+ const before = state().approval;
103
+ // Asked again for the record shown — after a save, a decision, a load — what it told stays meanwhile: what waits stays locked.
104
+ host.set({ approval: before && before.state === 'told' ? { ...before, state: 'asking' } : asking });
105
+ try {
106
+ const told = await source.approvals({ model, recordId, ...user() });
107
+ if (!shown() || seq !== asks)
108
+ return;
109
+ host.set({ approval: approvalOf(told, !!source.decide) });
110
+ }
111
+ catch (error) {
112
+ if (!shown() || seq !== asks)
113
+ return;
114
+ // Not told: nothing locked by it, and the page says they could not be read.
115
+ host.set({ approval: { ...asking, state: 'failed', error: saveProblemOf(error).message } });
116
+ }
117
+ }
118
+ async function decide(level, decision, reason) {
119
+ const source = host.dataSource;
120
+ const recordId = state().recordId;
121
+ if (!source?.decide || recordId == null || !model)
122
+ throw new Error('The data source has no decide()');
123
+ const now = state().approval;
124
+ if (!now?.mine.includes(level))
125
+ throw new Error(fill(messages.approvalNotYours, { level: nameOf(level) }));
126
+ const why = typeof reason === 'string' ? reason.trim() : '';
127
+ if (decision === 'rejected' && !why)
128
+ throw new Error(messages.approvalReasonNeeded);
129
+ // One decision at a time: a second press while one is on its way — its save, its answer — is the same press.
130
+ if (deciding || now.deciding)
131
+ return;
132
+ deciding = true;
133
+ try {
134
+ await decided(source.decide.bind(source), recordId, level, decision, why);
135
+ }
136
+ finally {
137
+ deciding = false;
138
+ }
139
+ }
140
+ async function decided(send, recordId, level, decision, why) {
141
+ const shown = form.recordShown();
142
+ // Decided on the record as it is saved: what is typed read, and the person's changes saved, first.
143
+ const refused = await saveFirst();
144
+ if (refused !== null)
145
+ throw new Error(refused);
146
+ if (!shown())
147
+ return;
148
+ const at = state().approval;
149
+ if (!at)
150
+ return;
151
+ host.set({ approval: { ...at, deciding: { level, decision } } });
152
+ try {
153
+ const answer = await send({ model, recordId, level, decision, ...(why ? { reason: why } : {}), ...user() });
154
+ if (!shown())
155
+ return;
156
+ const after = state().approval;
157
+ host.set({ approval: answer && Array.isArray(answer.levels) ? approvalOf(answer, true) : after ? { ...after, deciding: null } : null });
158
+ }
159
+ catch (error) {
160
+ const after = state().approval;
161
+ if (shown() && after)
162
+ host.set({ approval: { ...after, deciding: null } });
163
+ throw new Error(saveProblemOf(error).message, { cause: error });
164
+ }
165
+ // The decision may have moved the record on — its state, what may be changed: loaded again, its approvals with it.
166
+ if (shown())
167
+ await host.reload();
168
+ }
169
+ function locks(name) {
170
+ const own = page.approval;
171
+ return !!own && own.lock !== false && state().approval?.status === 'waiting' && !(own.open ?? []).includes(name);
172
+ }
173
+ return {
174
+ ask,
175
+ decide,
176
+ locks,
177
+ lockWords: (name) => (locks(name) ? messages.approvalLocked : null),
178
+ };
179
+ }
180
+ //# sourceMappingURL=form-approvals.mjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"form-approvals.mjs","sourceRoot":"","sources":["../../../../../../libs/core/src/lib/record/form-approvals.ts"],"names":[],"mappings":"AAEA,OAAO,EAAE,aAAa,EAAE,MAAM,eAAe,CAAC;AAG9C,OAAO,EAAE,IAAI,EAAiB,MAAM,YAAY,CAAC;AACjD,OAAO,EAAE,QAAQ,EAAE,MAAM,aAAa,CAAC;AAEvC,OAAO,EAAE,SAAS,EAAE,SAAS,EAAE,OAAO,EAAE,QAAQ,EAAE,MAAM,SAAS,CAAC;AAClE,OAAO,EAAE,WAAW,EAAE,MAAM,WAAW,CAAC;AAoCxC,MAAM,MAAM,GAAkC,CAAC,SAAS,EAAE,OAAO,EAAE,UAAU,EAAE,UAAU,EAAE,SAAS,CAAC,CAAC;AACtG,MAAM,IAAI,GAAG,CAAC,KAAc,EAAmB,EAAE,CAAC,OAAO,KAAK,KAAK,QAAQ,IAAI,KAAK,CAAC,IAAI,EAAE,KAAK,EAAE,CAAC;AAEnG,+HAA+H;AAC/H,SAAS,OAAO,CAAC,IAAa;IAC5B,MAAM,KAAK,GAAG,IAAqC,CAAC;IACpD,IAAI,CAAC,KAAK,IAAI,CAAC,IAAI,CAAC,KAAK,CAAC,EAAE,CAAC,IAAI,CAAC,MAAM,CAAC,QAAQ,CAAC,KAAK,CAAC,KAA2B,CAAC;QAAE,OAAO,IAAI,CAAC;IAClG,MAAM,SAAS,GAAe,CAAC,KAAK,CAAC,OAAO,CAAC,KAAK,CAAC,SAAS,CAAC,CAAC,CAAC,CAAC,KAAK,CAAC,SAAS,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,MAAM,CAAC,CAAC,GAAG,EAAmB,EAAE,CAAC,CAAC,CAAC,GAAG,IAAI,IAAI,CAAC,GAAG,CAAC,IAAI,CAAC,CAAC,CAAC;IAChJ,OAAO,EAAE,EAAE,EAAE,KAAK,CAAC,EAAE,EAAE,IAAI,EAAE,IAAI,CAAC,KAAK,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC,KAAK,CAAC,IAAI,CAAC,CAAC,CAAC,KAAK,CAAC,EAAE,EAAE,SAAS,EAAE,KAAK,EAAE,KAAK,CAAC,KAAK,KAAK,KAAK,CAAC,CAAC,CAAC,KAAK,CAAC,CAAC,CAAC,KAAK,EAAE,KAAK,EAAE,KAAK,CAAC,KAA2B,EAAE,CAAC;AAC7K,CAAC;AAED,6GAA6G;AAC7G,MAAM,UAAU,cAAc,CAAC,MAAgC;IAC7D,MAAM,KAAK,GAAG,MAAM,CAAC,MAAM,CAAC,CAAC,KAAK,EAAE,EAAE,CAAC,KAAK,CAAC,KAAK,KAAK,SAAS,CAAC,CAAC;IAClE,IAAI,CAAC,KAAK,CAAC,MAAM;QAAE,OAAO,MAAM,CAAC;IACjC,IAAI,KAAK,CAAC,IAAI,CAAC,CAAC,KAAK,EAAE,EAAE,CAAC,KAAK,CAAC,KAAK,KAAK,UAAU,CAAC;QAAE,OAAO,UAAU,CAAC;IACzE,IAAI,KAAK,CAAC,IAAI,CAAC,CAAC,KAAK,EAAE,EAAE,CAAC,KAAK,CAAC,KAAK,KAAK,SAAS,CAAC;QAAE,OAAO,SAAS,CAAC;IACvE,IAAI,KAAK,CAAC,KAAK,CAAC,CAAC,KAAK,EAAE,EAAE,CAAC,KAAK,CAAC,KAAK,KAAK,UAAU,CAAC;QAAE,OAAO,UAAU,CAAC;IAC1E,OAAO,QAAQ,CAAC;AAClB,CAAC;AAED,4IAA4I;AAC5I,MAAM,UAAU,UAAU,CAAC,IAAkC,EAAE,OAAgB;IAC7E,MAAM,MAAM,GAAG,CAAC,KAAK,CAAC,OAAO,CAAC,IAAI,EAAE,MAAM,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC,MAAM,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,GAAG,CAAC,OAAO,CAAC,CAAC,MAAM,CAAC,CAAC,KAAK,EAA0B,EAAE,CAAC,KAAK,KAAK,IAAI,CAAC,CAAC;IACvI,MAAM,OAAO,GAAG,CAAC,KAAK,CAAC,OAAO,CAAC,IAAI,EAAE,OAAO,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC,OAAO,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,MAAM,CAAC,CAAC,KAAK,EAA0B,EAAE,CAAC,CAAC,CAAC,KAAK,IAAI,IAAI,CAAC,KAAK,CAAC,EAAE,CAAC,IAAI,IAAI,CAAC,KAAK,CAAC,EAAE,CAAC,IAAI,CAAC,UAAU,EAAE,UAAU,EAAE,WAAW,CAAC,CAAC,QAAQ,CAAC,KAAK,CAAC,QAAQ,CAAC,CAAC,CAAC;IAC5N,MAAM,OAAO,GAAG,IAAI,GAAG,CAAC,MAAM,CAAC,MAAM,CAAC,CAAC,KAAK,EAAE,EAAE,CAAC,KAAK,CAAC,KAAK,KAAK,SAAS,CAAC,CAAC,GAAG,CAAC,CAAC,KAAK,EAAE,EAAE,CAAC,KAAK,CAAC,EAAE,CAAC,CAAC,CAAC;IACtG,MAAM,IAAI,GAAG,OAAO,CAAC,CAAC,CAAC,CAAC,GAAG,IAAI,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,CAAC,IAAI,EAAE,IAAI,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,MAAM,CAAC,CAAC,EAAE,EAAE,EAAE,CAAC,OAAO,CAAC,GAAG,CAAC,EAAE,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,EAAE,CAAC;IACvH,OAAO;QACL,KAAK,EAAE,MAAM;QACb,MAAM,EAAE,cAAc,CAAC,MAAM,CAAC;QAC9B,MAAM;QACN,QAAQ,EAAE,IAAI,EAAE,QAAQ,KAAK,IAAI;QACjC,OAAO;QACP,IAAI;QACJ,GAAG,CAAC,IAAI,CAAC,IAAI,EAAE,GAAG,CAAC,CAAC,CAAC,CAAC,EAAE,GAAG,EAAE,IAAI,CAAC,GAAG,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC;QAC7C,QAAQ,EAAE,IAAI;KACf,CAAC;AACJ,CAAC;AAED,qDAAqD;AACrD,MAAM,MAAM,GAAiB,EAAE,KAAK,EAAE,QAAQ,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,EAAE,EAAE,EAAE,QAAQ,EAAE,KAAK,EAAE,OAAO,EAAE,EAAE,EAAE,IAAI,EAAE,EAAE,EAAE,QAAQ,EAAE,IAAI,EAAE,CAAC;AAErI;;;GAGG;AACH,MAAM,UAAU,YAAY,CAAC,EAAU,EAAE,QAAkB,EAAE,IAAU;IACrE,MAAM,EAAE,GAAG,IAAI,CAAC,KAAK,CAAC,EAAE,CAAC,CAAC;IAC1B,IAAI,MAAM,CAAC,KAAK,CAAC,EAAE,CAAC;QAAE,OAAO,EAAE,CAAC;IAChC,MAAM,GAAG,GAAY,IAAI,CAAC,IAAI,EAAE,SAAS,CAAC;IAC1C,MAAM,KAAK,GAA0B,GAAG,KAAK,KAAK,IAAI,GAAG,KAAK,KAAK,IAAI,GAAG,KAAK,KAAK,CAAC,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC,QAAQ,CAAC,SAAS,CAAC;IAChH,MAAM,IAAI,GAAG,IAAI,CAAC,QAAQ,CAAC;IAC3B,MAAM,IAAI,GAAG,QAAQ,CAAC,EAAE,EAAE,IAAI,CAAC,CAAC;IAChC,MAAM,KAAK,GAAG,GAAG,QAAQ,CAAC,OAAO,CAAC,IAAI,CAAC,EAAE,QAAQ,EAAE,KAAK,CAAC,GAAG,KAAK,KAAK,KAAK,CAAC,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC,IAAI,GAAG,SAAS,CAAC,IAAI,CAAC,EAAE,CAAC;IAC7G,IAAI,CAAC,IAAI,IAAI,SAAS,CAAC,IAAI,CAAC;QAAE,OAAO,KAAK,CAAC;IAC3C,MAAM,IAAI,GAAG,IAAI,IAAI,CAAC,cAAc,CAAC,QAAQ,CAAC,MAAM,KAAK,IAAI,CAAC,CAAC,CAAC,OAAO,CAAC,CAAC,CAAC,QAAQ,CAAC,MAAM,EAAE,EAAE,QAAQ,EAAE,IAAI,EAAE,YAAY,EAAE,OAAO,EAAE,CAAC,CAAC,aAAa,CAAC,EAAE,CAAC,CAAC,IAAI,CAAC,CAAC,IAAI,EAAE,EAAE,CAAC,IAAI,CAAC,IAAI,KAAK,cAAc,CAAC,EAAE,KAAK,CAAC;IAC5M,OAAO,IAAI,CAAC,CAAC,CAAC,GAAG,KAAK,IAAI,IAAI,EAAE,CAAC,CAAC,CAAC,KAAK,CAAC;AAC3C,CAAC;AAED,sGAAsG;AACtG,MAAM,UAAU,aAAa,CAAC,IAAmB;IAC/C,MAAM,EAAE,IAAI,EAAE,QAAQ,EAAE,GAAG,IAAI,CAAC;IAChC,MAAM,IAAI,GAAG,IAAI,CAAC,IAAI,CAAC;IACvB,gGAAgG;IAChG,IAAI,IAAI,GAAG,CAAC,CAAC;IACb,mHAAmH;IACnH,IAAI,QAAQ,GAAG,KAAK,CAAC;IACrB,MAAM,IAAI,GAAG,GAAG,EAAE;QAChB,MAAM,MAAM,GAAG,IAAI,CAAC,IAAI,EAAE,CAAC;QAC3B,OAAO,MAAM,CAAC,CAAC,CAAC,EAAE,IAAI,EAAE,EAAE,EAAE,EAAE,MAAM,CAAC,EAAE,EAAE,GAAG,CAAC,MAAM,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,IAAI,EAAE,MAAM,CAAC,IAAI,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC;IAClG,CAAC,CAAC;IACF,MAAM,KAAK,GAAG,GAAG,EAAE,CAAC,IAAI,CAAC,QAAQ,EAAE,CAAC;IACpC,MAAM,KAAK,GAAG,IAAI,CAAC,IAAI,CAAC,IAAI,KAAK,QAAQ,CAAC,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC,CAAC,EAAE,CAAC;IACjE,MAAM,MAAM,GAAG,CAAC,KAAa,EAAE,EAAE,CAAC,KAAK,EAAE,CAAC,QAAQ,EAAE,MAAM,CAAC,IAAI,CAAC,CAAC,GAAG,EAAE,EAAE,CAAC,GAAG,CAAC,EAAE,KAAK,KAAK,CAAC,EAAE,IAAI,IAAI,KAAK,CAAC;IAE1G,kHAAkH;IAClH,KAAK,UAAU,SAAS;QACtB,MAAM,IAAI,CAAC,MAAM,EAAE,CAAC;QACpB,MAAM,GAAG,GAAG,KAAK,EAAE,CAAC;QACpB,IAAI,CAAC,GAAG,CAAC,KAAK,CAAC,MAAM,IAAI,CAAC,MAAM,CAAC,IAAI,CAAC,GAAG,CAAC,MAAM,CAAC,CAAC,MAAM,IAAI,CAAC,CAAC,GAAG,CAAC,OAAO,IAAI,EAAE,CAAC,CAAC,IAAI,CAAC,WAAW,CAAC;YAAE,OAAO,IAAI,CAAC;QAChH,IAAI,MAAM,IAAI,CAAC,IAAI,EAAE;YAAE,OAAO,IAAI,CAAC;QACnC,MAAM,KAAK,GAAG,KAAK,EAAE,CAAC;QACtB,OAAO,KAAK,CAAC,WAAW,EAAE,OAAO,IAAI,KAAK,CAAC,KAAK,IAAI,MAAM,CAAC,MAAM,CAAC,KAAK,CAAC,MAAM,CAAC,CAAC,CAAC,CAAC,IAAI,EAAE,CAAC;IAC3F,CAAC;IAED,KAAK,UAAU,GAAG;QAChB,MAAM,MAAM,GAAG,IAAI,CAAC,UAAU,CAAC;QAC/B,MAAM,QAAQ,GAAG,KAAK,EAAE,CAAC,QAAQ,CAAC;QAClC,IAAI,CAAC,MAAM,EAAE,SAAS,IAAI,QAAQ,IAAI,IAAI,IAAI,CAAC,KAAK,EAAE,CAAC;YACrD,IAAI,KAAK,EAAE,CAAC,QAAQ;gBAAE,IAAI,CAAC,GAAG,CAAC,EAAE,QAAQ,EAAE,IAAI,EAAE,CAAC,CAAC;YACnD,OAAO;QACT,CAAC;QACD,MAAM,KAAK,GAAG,IAAI,CAAC,WAAW,EAAE,CAAC;QACjC,MAAM,GAAG,GAAG,EAAE,IAAI,CAAC;QACnB,MAAM,MAAM,GAAG,KAAK,EAAE,CAAC,QAAQ,CAAC;QAChC,+HAA+H;QAC/H,IAAI,CAAC,GAAG,CAAC,EAAE,QAAQ,EAAE,MAAM,IAAI,MAAM,CAAC,KAAK,KAAK,MAAM,CAAC,CAAC,CAAC,EAAE,GAAG,MAAM,EAAE,KAAK,EAAE,QAAQ,EAAE,CAAC,CAAC,CAAC,MAAM,EAAE,CAAC,CAAC;QACpG,IAAI,CAAC;YACH,MAAM,IAAI,GAAG,MAAM,MAAM,CAAC,SAAS,CAAC,EAAE,KAAK,EAAE,QAAQ,EAAE,GAAG,IAAI,EAAE,EAAE,CAAC,CAAC;YACpE,IAAI,CAAC,KAAK,EAAE,IAAI,GAAG,KAAK,IAAI;gBAAE,OAAO;YACrC,IAAI,CAAC,GAAG,CAAC,EAAE,QAAQ,EAAE,UAAU,CAAC,IAAI,EAAE,CAAC,CAAC,MAAM,CAAC,MAAM,CAAC,EAAE,CAAC,CAAC;QAC5D,CAAC;QAAC,OAAO,KAAK,EAAE,CAAC;YACf,IAAI,CAAC,KAAK,EAAE,IAAI,GAAG,KAAK,IAAI;gBAAE,OAAO;YACrC,4EAA4E;YAC5E,IAAI,CAAC,GAAG,CAAC,EAAE,QAAQ,EAAE,EAAE,GAAG,MAAM,EAAE,KAAK,EAAE,QAAQ,EAAE,KAAK,EAAE,aAAa,CAAC,KAAK,CAAC,CAAC,OAAO,EAAE,EAAE,CAAC,CAAC;QAC9F,CAAC;IACH,CAAC;IAED,KAAK,UAAU,MAAM,CAAC,KAAa,EAAE,QAA0B,EAAE,MAAe;QAC9E,MAAM,MAAM,GAAG,IAAI,CAAC,UAAU,CAAC;QAC/B,MAAM,QAAQ,GAAG,KAAK,EAAE,CAAC,QAAQ,CAAC;QAClC,IAAI,CAAC,MAAM,EAAE,MAAM,IAAI,QAAQ,IAAI,IAAI,IAAI,CAAC,KAAK;YAAE,MAAM,IAAI,KAAK,CAAC,iCAAiC,CAAC,CAAC;QACtG,MAAM,GAAG,GAAG,KAAK,EAAE,CAAC,QAAQ,CAAC;QAC7B,IAAI,CAAC,GAAG,EAAE,IAAI,CAAC,QAAQ,CAAC,KAAK,CAAC;YAAE,MAAM,IAAI,KAAK,CAAC,IAAI,CAAC,QAAQ,CAAC,gBAAgB,EAAE,EAAE,KAAK,EAAE,MAAM,CAAC,KAAK,CAAC,EAAE,CAAC,CAAC,CAAC;QAC3G,MAAM,GAAG,GAAG,OAAO,MAAM,KAAK,QAAQ,CAAC,CAAC,CAAC,MAAM,CAAC,IAAI,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC;QAC5D,IAAI,QAAQ,KAAK,UAAU,IAAI,CAAC,GAAG;YAAE,MAAM,IAAI,KAAK,CAAC,QAAQ,CAAC,oBAAoB,CAAC,CAAC;QACpF,6GAA6G;QAC7G,IAAI,QAAQ,IAAI,GAAG,CAAC,QAAQ;YAAE,OAAO;QACrC,QAAQ,GAAG,IAAI,CAAC;QAChB,IAAI,CAAC;YACH,MAAM,OAAO,CAAC,MAAM,CAAC,MAAM,CAAC,IAAI,CAAC,MAAM,CAAC,EAAE,QAAQ,EAAE,KAAK,EAAE,QAAQ,EAAE,GAAG,CAAC,CAAC;QAC5E,CAAC;gBAAS,CAAC;YACT,QAAQ,GAAG,KAAK,CAAC;QACnB,CAAC;IACH,CAAC;IAED,KAAK,UAAU,OAAO,CAAC,IAAuC,EAAE,QAA4C,EAAE,KAAa,EAAE,QAA0B,EAAE,GAAW;QAClK,MAAM,KAAK,GAAG,IAAI,CAAC,WAAW,EAAE,CAAC;QACjC,mGAAmG;QACnG,MAAM,OAAO,GAAG,MAAM,SAAS,EAAE,CAAC;QAClC,IAAI,OAAO,KAAK,IAAI;YAAE,MAAM,IAAI,KAAK,CAAC,OAAO,CAAC,CAAC;QAC/C,IAAI,CAAC,KAAK,EAAE;YAAE,OAAO;QACrB,MAAM,EAAE,GAAG,KAAK,EAAE,CAAC,QAAQ,CAAC;QAC5B,IAAI,CAAC,EAAE;YAAE,OAAO;QAChB,IAAI,CAAC,GAAG,CAAC,EAAE,QAAQ,EAAE,EAAE,GAAG,EAAE,EAAE,QAAQ,EAAE,EAAE,KAAK,EAAE,QAAQ,EAAE,EAAE,EAAE,CAAC,CAAC;QACjE,IAAI,CAAC;YACH,MAAM,MAAM,GAAG,MAAM,IAAI,CAAC,EAAE,KAAK,EAAE,QAAQ,EAAE,KAAK,EAAE,QAAQ,EAAE,GAAG,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,MAAM,EAAE,GAAG,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC,EAAE,GAAG,IAAI,EAAE,EAAE,CAAC,CAAC;YAC5G,IAAI,CAAC,KAAK,EAAE;gBAAE,OAAO;YACrB,MAAM,KAAK,GAAG,KAAK,EAAE,CAAC,QAAQ,CAAC;YAC/B,IAAI,CAAC,GAAG,CAAC,EAAE,QAAQ,EAAE,MAAM,IAAI,KAAK,CAAC,OAAO,CAAC,MAAM,CAAC,MAAM,CAAC,CAAC,CAAC,CAAC,UAAU,CAAC,MAAM,EAAE,IAAI,CAAC,CAAC,CAAC,CAAC,KAAK,CAAC,CAAC,CAAC,EAAE,GAAG,KAAK,EAAE,QAAQ,EAAE,IAAI,EAAE,CAAC,CAAC,CAAC,IAAI,EAAE,CAAC,CAAC;QAC1I,CAAC;QAAC,OAAO,KAAK,EAAE,CAAC;YACf,MAAM,KAAK,GAAG,KAAK,EAAE,CAAC,QAAQ,CAAC;YAC/B,IAAI,KAAK,EAAE,IAAI,KAAK;gBAAE,IAAI,CAAC,GAAG,CAAC,EAAE,QAAQ,EAAE,EAAE,GAAG,KAAK,EAAE,QAAQ,EAAE,IAAI,EAAE,EAAE,CAAC,CAAC;YAC3E,MAAM,IAAI,KAAK,CAAC,aAAa,CAAC,KAAK,CAAC,CAAC,OAAO,EAAE,EAAE,KAAK,EAAE,KAAK,EAAE,CAAC,CAAC;QAClE,CAAC;QACD,mHAAmH;QACnH,IAAI,KAAK,EAAE;YAAE,MAAM,IAAI,CAAC,MAAM,EAAE,CAAC;IACnC,CAAC;IAED,SAAS,KAAK,CAAC,IAAY;QACzB,MAAM,GAAG,GAAG,IAAI,CAAC,QAAQ,CAAC;QAC1B,OAAO,CAAC,CAAC,GAAG,IAAI,GAAG,CAAC,IAAI,KAAK,KAAK,IAAI,KAAK,EAAE,CAAC,QAAQ,EAAE,MAAM,KAAK,SAAS,IAAI,CAAC,CAAC,GAAG,CAAC,IAAI,IAAI,EAAE,CAAC,CAAC,QAAQ,CAAC,IAAI,CAAC,CAAC;IACnH,CAAC;IAED,OAAO;QACL,GAAG;QACH,MAAM;QACN,KAAK;QACL,SAAS,EAAE,CAAC,IAAI,EAAE,EAAE,CAAC,CAAC,KAAK,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC,QAAQ,CAAC,cAAc,CAAC,CAAC,CAAC,IAAI,CAAC;KACpE,CAAC;AACJ,CAAC"}
@@ -8,8 +8,9 @@ import { type AppFunctions } from '../expression/app-functions.mjs';
8
8
  import { type AskedFor } from './compute.mjs';
9
9
  import { type FieldTone, type LineState } from './cells.mjs';
10
10
  import { type Messages } from './messages.mjs';
11
- import { type DataSource, type RecordChanges, type RecordPermissions, type RecordVersion, type ResolvedFilter, type SaveProblem, type BotReport, type SlotsResult, type UploadFile, type DuplicateMatch, type MergeResult, type SavedDraft } from './data-source.mjs';
11
+ import { type DataSource, type RecordChanges, type RecordPermissions, type RecordVersion, type ResolvedFilter, type SaveProblem, type BotReport, type SlotsResult, type UploadFile, type DuplicateMatch, type MergeResult, type SavedDraft, type ApprovalDecision } from './data-source.mjs';
12
12
  import { type FormLimits } from './taking.mjs';
13
+ import { type FormApproval } from './approving.mjs';
13
14
  import { type WaitsFor } from './waits.mjs';
14
15
  import { type FileUpload, type UploadHow } from './uploads.mjs';
15
16
  import { type Scheduler } from './scheduler.mjs';
@@ -123,6 +124,12 @@ export interface FormState {
123
124
  readonly key: string;
124
125
  readonly missing?: boolean;
125
126
  } | null;
127
+ /**
128
+ * The record's approvals, on a page with `approval` whose data source tells them (`approvals`): its levels and where
129
+ * each stands, what was decided, the levels this person may decide, and whether it waits — when its fields lock but
130
+ * for those the page leaves open. Null for a new record, or a page without them.
131
+ */
132
+ readonly approval?: FormApproval | null;
126
133
  }
127
134
  /** A response sent, as `FormState.sent` keeps it. */
128
135
  export interface SentResponse {
@@ -139,6 +146,11 @@ export interface SearchOptions {
139
146
  ids?: RecordId[];
140
147
  /** Only the records named exactly the query, in any case (`SearchRequest.exact`): a typed name read as a record. */
141
148
  exact?: boolean;
149
+ /**
150
+ * Only the records whose `field` holds the query exactly — a code read by a scanner, by the field holding the linked
151
+ * records' codes ("barcode") — asked through the search's filter, whatever their names.
152
+ */
153
+ field?: string;
142
154
  }
143
155
  /** Text left in a box that does not read, and why, as `FormState.unread` keeps it. */
144
156
  export interface UnreadText {
@@ -695,6 +707,23 @@ export interface Form {
695
707
  * show what it tells (`limits`). The form asks it itself as it opens, before its answers go, and after they went.
696
708
  */
697
709
  refreshLimits(): Promise<void>;
710
+ /**
711
+ * Ask the data source's `approvals` again for the record shown, and keep what it tells (`approval`). The form asks it
712
+ * itself as a saved record is shown, after each save, and after a decision.
713
+ */
714
+ refreshApprovals(): Promise<void>;
715
+ /**
716
+ * Decide at a level of the record's approvals this person may decide now (`approval.mine`): approved, or rejected
717
+ * with why — a rejection always says why. What is typed is read and the person's changes saved first; the source's
718
+ * `decide` takes it; the record is loaded again after, its approvals with it. Rejects in words where the level is not
719
+ * theirs to decide now, a rejection gives no reason, the save is refused, or the source refused the decision.
720
+ */
721
+ decide(level: string, decision: ApprovalDecision, reason?: string): Promise<void>;
722
+ /**
723
+ * What a field's lock says while the record waits for approval — locked but for the fields the page leaves open
724
+ * (`approval.open`) — or null where no approval locks it.
725
+ */
726
+ approvalLock(name: string): string | null;
698
727
  /**
699
728
  * Whether a file picked for a file or picture field is stored at once,
700
729
  * through the data source's `upload`. False without one: a file then
@@ -774,6 +803,8 @@ export interface Form {
774
803
  * makes each ready first, one at a time (a photo made smaller). A field of
775
804
  * one file keeps the last one picked. Gives each upload's id. A save waits
776
805
  * for those still going, and stops, under the field, for one that failed.
806
+ * `how.replaces`: the file is stored for bytes the field holds (a photo
807
+ * cropped), its link taking their place once stored, as a save's would.
777
808
  */
778
809
  upload(field: string, files: readonly UploadFile[], how?: UploadHow): string[];
779
810
  /**
@@ -8,8 +8,9 @@ import { type AppFunctions } from '../expression/app-functions';
8
8
  import { type AskedFor } from './compute';
9
9
  import { type FieldTone, type LineState } from './cells';
10
10
  import { type Messages } from './messages';
11
- import { type DataSource, type RecordChanges, type RecordPermissions, type RecordVersion, type ResolvedFilter, type SaveProblem, type BotReport, type SlotsResult, type UploadFile, type DuplicateMatch, type MergeResult, type SavedDraft } from './data-source';
11
+ import { type DataSource, type RecordChanges, type RecordPermissions, type RecordVersion, type ResolvedFilter, type SaveProblem, type BotReport, type SlotsResult, type UploadFile, type DuplicateMatch, type MergeResult, type SavedDraft, type ApprovalDecision } from './data-source';
12
12
  import { type FormLimits } from './taking';
13
+ import { type FormApproval } from './approving';
13
14
  import { type WaitsFor } from './waits';
14
15
  import { type FileUpload, type UploadHow } from './uploads';
15
16
  import { type Scheduler } from './scheduler';
@@ -123,6 +124,12 @@ export interface FormState {
123
124
  readonly key: string;
124
125
  readonly missing?: boolean;
125
126
  } | null;
127
+ /**
128
+ * The record's approvals, on a page with `approval` whose data source tells them (`approvals`): its levels and where
129
+ * each stands, what was decided, the levels this person may decide, and whether it waits — when its fields lock but
130
+ * for those the page leaves open. Null for a new record, or a page without them.
131
+ */
132
+ readonly approval?: FormApproval | null;
126
133
  }
127
134
  /** A response sent, as `FormState.sent` keeps it. */
128
135
  export interface SentResponse {
@@ -139,6 +146,11 @@ export interface SearchOptions {
139
146
  ids?: RecordId[];
140
147
  /** Only the records named exactly the query, in any case (`SearchRequest.exact`): a typed name read as a record. */
141
148
  exact?: boolean;
149
+ /**
150
+ * Only the records whose `field` holds the query exactly — a code read by a scanner, by the field holding the linked
151
+ * records' codes ("barcode") — asked through the search's filter, whatever their names.
152
+ */
153
+ field?: string;
142
154
  }
143
155
  /** Text left in a box that does not read, and why, as `FormState.unread` keeps it. */
144
156
  export interface UnreadText {
@@ -695,6 +707,23 @@ export interface Form {
695
707
  * show what it tells (`limits`). The form asks it itself as it opens, before its answers go, and after they went.
696
708
  */
697
709
  refreshLimits(): Promise<void>;
710
+ /**
711
+ * Ask the data source's `approvals` again for the record shown, and keep what it tells (`approval`). The form asks it
712
+ * itself as a saved record is shown, after each save, and after a decision.
713
+ */
714
+ refreshApprovals(): Promise<void>;
715
+ /**
716
+ * Decide at a level of the record's approvals this person may decide now (`approval.mine`): approved, or rejected
717
+ * with why — a rejection always says why. What is typed is read and the person's changes saved first; the source's
718
+ * `decide` takes it; the record is loaded again after, its approvals with it. Rejects in words where the level is not
719
+ * theirs to decide now, a rejection gives no reason, the save is refused, or the source refused the decision.
720
+ */
721
+ decide(level: string, decision: ApprovalDecision, reason?: string): Promise<void>;
722
+ /**
723
+ * What a field's lock says while the record waits for approval — locked but for the fields the page leaves open
724
+ * (`approval.open`) — or null where no approval locks it.
725
+ */
726
+ approvalLock(name: string): string | null;
698
727
  /**
699
728
  * Whether a file picked for a file or picture field is stored at once,
700
729
  * through the data source's `upload`. False without one: a file then
@@ -774,6 +803,8 @@ export interface Form {
774
803
  * makes each ready first, one at a time (a photo made smaller). A field of
775
804
  * one file keeps the last one picked. Gives each upload's id. A save waits
776
805
  * for those still going, and stops, under the field, for one that failed.
806
+ * `how.replaces`: the file is stored for bytes the field holds (a photo
807
+ * cropped), its link taking their place once stored, as a save's would.
777
808
  */
778
809
  upload(field: string, files: readonly UploadFile[], how?: UploadHow): string[];
779
810
  /**
@@ -27,6 +27,8 @@ const record_words_1 = require("./record-words");
27
27
  const line_choices_1 = require("./line-choices");
28
28
  const data_source_1 = require("./data-source");
29
29
  const taking_1 = require("./taking");
30
+ const languages_1 = require("./languages");
31
+ const approving_1 = require("./approving");
30
32
  const drafts_1 = require("./drafts");
31
33
  const waits_1 = require("./waits");
32
34
  const uploads_1 = require("./uploads");
@@ -367,6 +369,7 @@ function innerForm(options, within, place, outer) {
367
369
  // Worked out once the form has its state, as the limits part comes (`limitsCode`).
368
370
  limits: null,
369
371
  resume: null,
372
+ approval: null,
370
373
  };
371
374
  started = true;
372
375
  /**
@@ -963,6 +966,10 @@ function innerForm(options, within, place, outer) {
963
966
  }
964
967
  if ((0, choice_options_1.hasOptionRules)(page))
965
968
  void track(optionsCode());
969
+ // A text in several languages is checked by the languages part: asked for at once, a save waiting for it.
970
+ const languagesCode = () => languages_1.formLanguagesPart.load().then(() => undefined, () => undefined);
971
+ if ((0, languages_1.hasLanguages)(page))
972
+ void track(languagesCode());
966
973
  /** A table line's choice's options as they are offered on that line: its list's once loaded, or its own, by their conditions once they came. */
967
974
  function lineOffered(lines, line, column, record) {
968
975
  if (optionKit)
@@ -1130,6 +1137,9 @@ function innerForm(options, within, place, outer) {
1130
1137
  const answer = answerOf(node);
1131
1138
  if (answer !== undefined && signedNow().has(answer))
1132
1139
  readonly = true;
1140
+ // A record waiting for approval: locked but for the fields the page leaves open (the approvals part).
1141
+ if (answer !== undefined && approvalKit?.locks(answer))
1142
+ readonly = true;
1133
1143
  return {
1134
1144
  invisible,
1135
1145
  readonly: readonly || (def !== undefined && lockedField(def)),
@@ -1768,6 +1778,15 @@ function innerForm(options, within, place, outer) {
1768
1778
  }
1769
1779
  /** A choice of a full option, picked by the person, refused under its field: the field keeps what it held. */
1770
1780
  const refusedFull = (name, value) => !!limitsKit?.refusedFull(name, value);
1781
+ // ---- a record's approvals (the approvals part: form-approvals.ts) ---------------------------------------------------
1782
+ /** The approvals made for this form from the approvals part, once it came: none before, nor on a page without them. */
1783
+ let approvalKit = null;
1784
+ /** Whether the form asks for approvals: a page with `approval` (a record's own, as its check says), its source telling them. */
1785
+ const keepsApprovals = !outer && !!page.approval && !!options.dataSource?.approvals;
1786
+ /** The approvals, made from their part once it came; null without them, or where it could not come. */
1787
+ const approvalsCode = () => keepsApprovals ? approving_1.formApprovalsPart.load().then((part) => (approvalKit ??= part.formApprovals({ form, dataSource: options.dataSource, messages, set, reload: () => load(true) })), () => null) : Promise.resolve(null);
1788
+ /** Ask the record shown's approvals, once their part came; nothing on a page without them. */
1789
+ const askApprovals = () => approvalsCode().then((kit) => kit?.ask());
1771
1790
  function scheduleAutosave() {
1772
1791
  const autosave = options.autosave;
1773
1792
  // A record the person may not change is never saved unasked: it would only be refused.
@@ -1914,6 +1933,12 @@ function innerForm(options, within, place, outer) {
1914
1933
  if (asked !== generation)
1915
1934
  return elsewhere();
1916
1935
  }
1936
+ // A text in several languages, checked in each once its part has come.
1937
+ if (!languages_1.formLanguagesPart.loaded && (0, languages_1.hasLanguages)(page)) {
1938
+ await languagesCode();
1939
+ if (asked !== generation)
1940
+ return elsewhere();
1941
+ }
1917
1942
  // Values the app's functions are asked for are waited for — those that failed asked again — so what is saved is worked out.
1918
1943
  if (asking.size || Object.values(state.asked).some((found) => found.failed !== undefined)) {
1919
1944
  set({ status: 'saving', error: null, saveProblem: null });
@@ -2037,6 +2062,9 @@ function innerForm(options, within, place, outer) {
2037
2062
  scheduleDraft();
2038
2063
  scheduleAutosave();
2039
2064
  }
2065
+ // A save may have sent it for approval, or moved it on: its approvals asked again.
2066
+ if (keepsApprovals)
2067
+ void track(askApprovals());
2040
2068
  // The record as this save left it: a change made while it was on its way is no value of it.
2041
2069
  emit('save', { recordId: state.recordId, values: baseline });
2042
2070
  void track(moment('on.afterSave', page.on?.afterSave, chain, false));
@@ -2243,7 +2271,7 @@ function innerForm(options, within, place, outer) {
2243
2271
  throw new Error(`"${name}" cannot make records: its data source has no create, or it is not a link`);
2244
2272
  return track(source.create({ model, name: typed.trim(), ...(Object.keys(values).length ? { values } : {}) }));
2245
2273
  }
2246
- async function runSearch(def, name, ctx, query, limit, { model, ids, exact } = {}) {
2274
+ async function runSearch(def, name, ctx, query, limit, { model, ids, exact, field } = {}) {
2247
2275
  // Formatted text names people and records in place (@mention, #4127): searched by the model they are of.
2248
2276
  const byModel = def.type === 'reference' || def.type === 'json' || def.type === 'html';
2249
2277
  if (def.type !== 'many2one' && def.type !== 'many2many' && !byModel) {
@@ -2256,9 +2284,11 @@ function innerForm(options, within, place, outer) {
2256
2284
  return [];
2257
2285
  // Every valueFrom, in groups too, becomes the value it names before the search goes out.
2258
2286
  const own = byModel || !def.filter ? undefined : (0, filter_1.resolveFilter)(def.filter, ctx);
2259
- const filter = ids ? [...(own ?? []), { field: 'id', op: 'in', value: ids }] : own;
2287
+ const kept = ids ? [...(own ?? []), { field: 'id', op: 'in', value: ids }] : own;
2288
+ // A code read by a scanner: the records whose field holds it, whatever their names.
2289
+ const filter = field ? [...(kept ?? []), { field, op: '=', value: query }] : kept;
2260
2290
  const relation = byModel ? model : def.relation;
2261
- return track(source.search({ model: relation, query, ...(filter ? { filter } : {}), limit, ...(exact ? { exact: true } : {}) }));
2291
+ return track(source.search({ model: relation, query: field ? '' : query, ...(filter ? { filter } : {}), limit, ...(exact ? { exact: true } : {}) }));
2262
2292
  }
2263
2293
  function lineField(field) {
2264
2294
  const def = fieldDef(field);
@@ -2299,6 +2329,9 @@ function innerForm(options, within, place, outer) {
2299
2329
  loadedOnce = true;
2300
2330
  if (asksLimits())
2301
2331
  void track(askLimits());
2332
+ // Its approvals: where it stands, what was decided, what this person may decide — its fields locked while it waits.
2333
+ if (keepsApprovals)
2334
+ void track(askApprovals());
2302
2335
  if (again)
2303
2336
  return;
2304
2337
  offerDraft();
@@ -2331,7 +2364,7 @@ function innerForm(options, within, place, outer) {
2331
2364
  found = null;
2332
2365
  serverKey = null;
2333
2366
  draftEpoch++;
2334
- const fresh = { dirty: [], errors: {}, warning: null, warningField: null, error: null, draft: null, step: steps[0] ?? null, skipped: [], saveProblem: null, version: null, can: null, conflict: null, setAside: null, unsent: [], unread: {}, takenOut: {}, resume: null };
2367
+ const fresh = { dirty: [], errors: {}, warning: null, warningField: null, error: null, draft: null, step: steps[0] ?? null, skipped: [], saveProblem: null, version: null, can: null, conflict: null, setAside: null, unsent: [], unread: {}, takenOut: {}, resume: null, approval: null };
2335
2368
  if (id != null) {
2336
2369
  set({ ...fresh, recordId: id, status: 'idle' });
2337
2370
  await load();
@@ -2750,7 +2783,7 @@ function innerForm(options, within, place, outer) {
2750
2783
  // Nothing of the record changed; what shows of it may have.
2751
2784
  set({});
2752
2785
  },
2753
- fieldReadonly: (name) => lockedField(fieldDef(name)) || state.can?.edit === false || state.can?.readonlyFields?.includes(name) === true || signedNow().has(name),
2786
+ fieldReadonly: (name) => lockedField(fieldDef(name)) || state.can?.edit === false || state.can?.readonlyFields?.includes(name) === true || signedNow().has(name) || !!approvalKit?.locks(name),
2754
2787
  loadChoices: (field) => loadChoices(field),
2755
2788
  loadDefinitions,
2756
2789
  addDefinition,
@@ -3248,6 +3281,14 @@ function innerForm(options, within, place, outer) {
3248
3281
  return saved;
3249
3282
  },
3250
3283
  refreshLimits: () => askLimits(),
3284
+ refreshApprovals: askApprovals,
3285
+ async decide(level, decision, reason) {
3286
+ const kit = await approvalsCode();
3287
+ if (!kit)
3288
+ throw new Error('No approvals on this page');
3289
+ return track(kit.decide(level, decision, reason));
3290
+ },
3291
+ approvalLock: (name) => approvalKit?.lockWords(name) ?? null,
3251
3292
  async reloadTheirs() {
3252
3293
  // A save on its way first: its answer is not dropped by the load.
3253
3294
  const asked = shownNow();
@@ -3432,6 +3473,9 @@ function innerForm(options, within, place, outer) {
3432
3473
  // choice's places as the source tells them: a record's own once it is loaded, its own choices not counted against it.
3433
3474
  if (keepsLimits())
3434
3475
  void track(limitsCode());
3476
+ // A record's approvals: their part asked for as the form is made, ready by the time its record is loaded.
3477
+ if (keepsApprovals)
3478
+ void track(approvalsCode());
3435
3479
  // A new form has its values now — a draft continued from its key once it is laid — and opens once whoever made it
3436
3480
  // has had the chance to listen.
3437
3481
  if (state.recordId == null) {
@@ -3602,7 +3646,14 @@ function indexLayout(root, functions) {
3602
3646
  ...(does.length ? { does } : {}),
3603
3647
  source,
3604
3648
  });
3605
- // A button's menu: each item a button of the record, hidden with it.
3649
+ // A menu's built-in Archive shows while the record is active, Unarchive while it is not.
3650
+ const builtin = source.builtin;
3651
+ if (builtin === 'archive' || builtin === 'unarchive') {
3652
+ const own = index.get(source.id);
3653
+ const mine = own.invisible;
3654
+ own.invisible = { ...mine, evaluate: (context, env) => (context['active'] === false) !== (builtin === 'unarchive') || mine.evaluate(context, env) };
3655
+ }
3656
+ // A button's menu, and a menu inside a menu: each item a button of the record, hidden with it.
3606
3657
  if (kind === 'button')
3607
3658
  for (const item of source.menu ?? [])
3608
3659
  add(item, source.id, 'button', { invisible: item.invisible });
@@ -3711,16 +3762,10 @@ function indexLayout(root, functions) {
3711
3762
  walk(head?.band?.children ?? [], root.id);
3712
3763
  if (root.sidePanel)
3713
3764
  add(root.sidePanel, root.id, 'other', { invisible: root.sidePanel.invisible });
3714
- // The gear menu's items are buttons of the record; Archive shows while it is active, Unarchive while it is not.
3715
- for (const item of root.toolbar?.menu ?? []) {
3765
+ // The gear menu's items are buttons of the record — those of a menu inside it too, hidden with the item that opens
3766
+ // it (add's own); Archive shows while it is active, Unarchive while it is not.
3767
+ for (const item of root.toolbar?.menu ?? [])
3716
3768
  add(item, root.id, 'button', { invisible: item.invisible });
3717
- if (item.builtin === 'archive' || item.builtin === 'unarchive') {
3718
- const own = index.get(item.id);
3719
- const mine = own.invisible;
3720
- const archived = item.builtin === 'unarchive';
3721
- own.invisible = { ...mine, evaluate: (context, env) => (context['active'] === false) !== archived || mine.evaluate(context, env) };
3722
- }
3723
- }
3724
3769
  if (root.attachmentPreview)
3725
3770
  add({ id: '#preview', ...root.attachmentPreview }, root.id, 'other', { invisible: root.attachmentPreview.invisible });
3726
3771
  }