@fieldia/core 0.27.0 → 0.29.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/README.md +148 -5
- package/package.json +1 -1
- package/schema/page.schema.json +266 -7
- package/src/index.d.mts +10 -2
- package/src/index.d.ts +10 -2
- package/src/index.js +33 -2
- package/src/index.js.map +1 -1
- package/src/index.mjs +13 -2
- package/src/index.mjs.map +1 -1
- package/src/lib/format/approval-checks.d.mts +8 -0
- package/src/lib/format/approval-checks.d.ts +8 -0
- package/src/lib/format/approval-checks.js +29 -0
- package/src/lib/format/approval-checks.js.map +1 -0
- package/src/lib/format/approval-checks.mjs +26 -0
- package/src/lib/format/approval-checks.mjs.map +1 -0
- package/src/lib/format/approval.d.mts +20 -0
- package/src/lib/format/approval.d.ts +20 -0
- package/src/lib/format/approval.js +25 -0
- package/src/lib/format/approval.js.map +1 -0
- package/src/lib/format/approval.mjs +22 -0
- package/src/lib/format/approval.mjs.map +1 -0
- package/src/lib/format/assist-checks.d.mts +14 -0
- package/src/lib/format/assist-checks.d.ts +14 -0
- package/src/lib/format/assist-checks.js +68 -0
- package/src/lib/format/assist-checks.js.map +1 -0
- package/src/lib/format/assist-checks.mjs +64 -0
- package/src/lib/format/assist-checks.mjs.map +1 -0
- package/src/lib/format/assist.d.mts +41 -0
- package/src/lib/format/assist.d.ts +41 -0
- package/src/lib/format/assist.js +29 -0
- package/src/lib/format/assist.js.map +1 -0
- package/src/lib/format/assist.mjs +26 -0
- package/src/lib/format/assist.mjs.map +1 -0
- package/src/lib/format/editable-checks.d.mts +8 -0
- package/src/lib/format/editable-checks.d.ts +8 -0
- package/src/lib/format/editable-checks.js +38 -0
- package/src/lib/format/editable-checks.js.map +1 -0
- package/src/lib/format/editable-checks.mjs +35 -0
- package/src/lib/format/editable-checks.mjs.map +1 -0
- package/src/lib/format/field.d.mts +86 -0
- package/src/lib/format/field.d.ts +86 -0
- package/src/lib/format/field.js +56 -10
- package/src/lib/format/field.js.map +1 -1
- package/src/lib/format/field.mjs +55 -9
- package/src/lib/format/field.mjs.map +1 -1
- package/src/lib/format/languages-checks.d.mts +8 -0
- package/src/lib/format/languages-checks.d.ts +8 -0
- package/src/lib/format/languages-checks.js +40 -0
- package/src/lib/format/languages-checks.js.map +1 -0
- package/src/lib/format/languages-checks.mjs +37 -0
- package/src/lib/format/languages-checks.mjs.map +1 -0
- package/src/lib/format/later-checks.d.mts +4 -3
- package/src/lib/format/later-checks.d.ts +4 -3
- package/src/lib/format/later-checks.js +4 -3
- package/src/lib/format/later-checks.js.map +1 -1
- package/src/lib/format/later-checks.mjs +4 -3
- package/src/lib/format/later-checks.mjs.map +1 -1
- package/src/lib/format/layout.d.mts +123 -28
- package/src/lib/format/layout.d.ts +123 -28
- package/src/lib/format/layout.js +20 -4
- package/src/lib/format/layout.js.map +1 -1
- package/src/lib/format/layout.mjs +18 -2
- package/src/lib/format/layout.mjs.map +1 -1
- package/src/lib/format/layout.types.d.mts +2 -1
- package/src/lib/format/layout.types.d.ts +2 -1
- package/src/lib/format/list-editable.d.mts +20 -0
- package/src/lib/format/list-editable.d.ts +20 -0
- package/src/lib/format/list-editable.js +33 -0
- package/src/lib/format/list-editable.js.map +1 -0
- package/src/lib/format/list-editable.mjs +28 -0
- package/src/lib/format/list-editable.mjs.map +1 -0
- package/src/lib/format/page.d.mts +91 -9
- package/src/lib/format/page.d.ts +91 -9
- package/src/lib/format/page.js +4 -0
- package/src/lib/format/page.js.map +1 -1
- package/src/lib/format/page.mjs +4 -0
- package/src/lib/format/page.mjs.map +1 -1
- package/src/lib/format/photos-options.d.mts +29 -0
- package/src/lib/format/photos-options.d.ts +29 -0
- package/src/lib/format/photos-options.js +49 -0
- package/src/lib/format/photos-options.js.map +1 -0
- package/src/lib/format/photos-options.mjs +45 -0
- package/src/lib/format/photos-options.mjs.map +1 -0
- package/src/lib/format/references.js +31 -1
- package/src/lib/format/references.js.map +1 -1
- package/src/lib/format/references.mjs +31 -1
- package/src/lib/format/references.mjs.map +1 -1
- package/src/lib/format/scan-checks.d.mts +12 -0
- package/src/lib/format/scan-checks.d.ts +12 -0
- package/src/lib/format/scan-checks.js +89 -0
- package/src/lib/format/scan-checks.js.map +1 -0
- package/src/lib/format/scan-checks.mjs +86 -0
- package/src/lib/format/scan-checks.mjs.map +1 -0
- package/src/lib/format/scan-options.d.mts +84 -0
- package/src/lib/format/scan-options.d.ts +84 -0
- package/src/lib/format/scan-options.js +58 -0
- package/src/lib/format/scan-options.js.map +1 -0
- package/src/lib/format/scan-options.mjs +54 -0
- package/src/lib/format/scan-options.mjs.map +1 -0
- package/src/lib/format/upgrade.d.mts +4 -1
- package/src/lib/format/upgrade.d.ts +4 -1
- package/src/lib/format/upgrade.js +4 -1
- package/src/lib/format/upgrade.js.map +1 -1
- package/src/lib/format/upgrade.mjs +4 -1
- package/src/lib/format/upgrade.mjs.map +1 -1
- package/src/lib/format/version.d.mts +2 -2
- package/src/lib/format/version.d.ts +2 -2
- package/src/lib/format/version.js +2 -2
- package/src/lib/format/version.js.map +1 -1
- package/src/lib/format/version.mjs +2 -2
- package/src/lib/format/version.mjs.map +1 -1
- package/src/lib/record/approvals-part.d.mts +6 -0
- package/src/lib/record/approvals-part.d.ts +6 -0
- package/src/lib/record/approvals-part.js +14 -0
- package/src/lib/record/approvals-part.js.map +1 -0
- package/src/lib/record/approvals-part.mjs +7 -0
- package/src/lib/record/approvals-part.mjs.map +1 -0
- package/src/lib/record/approving.d.mts +45 -0
- package/src/lib/record/approving.d.ts +45 -0
- package/src/lib/record/approving.js +10 -0
- package/src/lib/record/approving.js.map +1 -0
- package/src/lib/record/approving.mjs +7 -0
- package/src/lib/record/approving.mjs.map +1 -0
- package/src/lib/record/check.js +7 -1
- package/src/lib/record/check.js.map +1 -1
- package/src/lib/record/check.mjs +7 -1
- package/src/lib/record/check.mjs.map +1 -1
- package/src/lib/record/data-source.d.mts +270 -0
- package/src/lib/record/data-source.d.ts +270 -0
- package/src/lib/record/data-source.js.map +1 -1
- package/src/lib/record/data-source.mjs.map +1 -1
- package/src/lib/record/day-words.d.mts +10 -0
- package/src/lib/record/day-words.d.ts +10 -0
- package/src/lib/record/day-words.js +23 -0
- package/src/lib/record/day-words.js.map +1 -0
- package/src/lib/record/day-words.mjs +20 -0
- package/src/lib/record/day-words.mjs.map +1 -0
- package/src/lib/record/files-part.d.mts +7 -0
- package/src/lib/record/files-part.d.ts +7 -0
- package/src/lib/record/files-part.js +167 -0
- package/src/lib/record/files-part.js.map +1 -0
- package/src/lib/record/files-part.mjs +163 -0
- package/src/lib/record/files-part.mjs.map +1 -0
- package/src/lib/record/filter.d.mts +5 -0
- package/src/lib/record/filter.d.ts +5 -0
- package/src/lib/record/filter.js +1 -0
- package/src/lib/record/filter.js.map +1 -1
- package/src/lib/record/filter.mjs +1 -1
- package/src/lib/record/filter.mjs.map +1 -1
- package/src/lib/record/form-approvals.d.mts +47 -0
- package/src/lib/record/form-approvals.d.ts +47 -0
- package/src/lib/record/form-approvals.js +186 -0
- package/src/lib/record/form-approvals.js.map +1 -0
- package/src/lib/record/form-approvals.mjs +180 -0
- package/src/lib/record/form-approvals.mjs.map +1 -0
- package/src/lib/record/form-limits.d.mts +5 -8
- package/src/lib/record/form-limits.d.ts +5 -8
- package/src/lib/record/form-limits.js +20 -21
- package/src/lib/record/form-limits.js.map +1 -1
- package/src/lib/record/form-limits.mjs +21 -22
- package/src/lib/record/form-limits.mjs.map +1 -1
- package/src/lib/record/form.d.mts +41 -8
- package/src/lib/record/form.d.ts +41 -8
- package/src/lib/record/form.js +119 -11
- package/src/lib/record/form.js.map +1 -1
- package/src/lib/record/form.mjs +119 -11
- package/src/lib/record/form.mjs.map +1 -1
- package/src/lib/record/languages-part.d.mts +32 -0
- package/src/lib/record/languages-part.d.ts +32 -0
- package/src/lib/record/languages-part.js +104 -0
- package/src/lib/record/languages-part.js.map +1 -0
- package/src/lib/record/languages-part.mjs +96 -0
- package/src/lib/record/languages-part.mjs.map +1 -0
- package/src/lib/record/languages.d.mts +26 -0
- package/src/lib/record/languages.d.ts +26 -0
- package/src/lib/record/languages.js +20 -0
- package/src/lib/record/languages.js.map +1 -0
- package/src/lib/record/languages.mjs +15 -0
- package/src/lib/record/languages.mjs.map +1 -0
- package/src/lib/record/limits.js +4 -2
- package/src/lib/record/limits.js.map +1 -1
- package/src/lib/record/limits.mjs +4 -2
- package/src/lib/record/limits.mjs.map +1 -1
- package/src/lib/record/locales/ar.js +6 -0
- package/src/lib/record/locales/ar.js.map +1 -1
- package/src/lib/record/locales/ar.mjs +6 -0
- package/src/lib/record/locales/ar.mjs.map +1 -1
- package/src/lib/record/locales/de.js +6 -0
- package/src/lib/record/locales/de.js.map +1 -1
- package/src/lib/record/locales/de.mjs +6 -0
- package/src/lib/record/locales/de.mjs.map +1 -1
- package/src/lib/record/locales/fr.js +6 -0
- package/src/lib/record/locales/fr.js.map +1 -1
- package/src/lib/record/locales/fr.mjs +6 -0
- package/src/lib/record/locales/fr.mjs.map +1 -1
- package/src/lib/record/memory-approvals.d.mts +47 -0
- package/src/lib/record/memory-approvals.d.ts +47 -0
- package/src/lib/record/memory-approvals.js +78 -0
- package/src/lib/record/memory-approvals.js.map +1 -0
- package/src/lib/record/memory-approvals.mjs +75 -0
- package/src/lib/record/memory-approvals.mjs.map +1 -0
- package/src/lib/record/memory-assist.d.mts +53 -0
- package/src/lib/record/memory-assist.d.ts +53 -0
- package/src/lib/record/memory-assist.js +519 -0
- package/src/lib/record/memory-assist.js.map +1 -0
- package/src/lib/record/memory-assist.mjs +513 -0
- package/src/lib/record/memory-assist.mjs.map +1 -0
- package/src/lib/record/memory-clusters.d.mts +18 -0
- package/src/lib/record/memory-clusters.d.ts +18 -0
- package/src/lib/record/memory-clusters.js +83 -0
- package/src/lib/record/memory-clusters.js.map +1 -0
- package/src/lib/record/memory-clusters.mjs +80 -0
- package/src/lib/record/memory-clusters.mjs.map +1 -0
- package/src/lib/record/memory-data-source.d.mts +43 -3
- package/src/lib/record/memory-data-source.d.ts +43 -3
- package/src/lib/record/memory-data-source.js +105 -19
- package/src/lib/record/memory-data-source.js.map +1 -1
- package/src/lib/record/memory-data-source.mjs +105 -19
- package/src/lib/record/memory-data-source.mjs.map +1 -1
- package/src/lib/record/memory-later.d.mts +1 -1
- package/src/lib/record/memory-later.d.ts +1 -1
- package/src/lib/record/memory-later.js +4 -1
- package/src/lib/record/memory-later.js.map +1 -1
- package/src/lib/record/memory-later.mjs +4 -1
- package/src/lib/record/memory-later.mjs.map +1 -1
- package/src/lib/record/messages.d.mts +24 -2
- package/src/lib/record/messages.d.ts +24 -2
- package/src/lib/record/messages.js +8 -4
- package/src/lib/record/messages.js.map +1 -1
- package/src/lib/record/messages.mjs +8 -4
- package/src/lib/record/messages.mjs.map +1 -1
- package/src/lib/record/option-rules.d.mts +7 -1
- package/src/lib/record/option-rules.d.ts +7 -1
- package/src/lib/record/option-rules.js +9 -3
- package/src/lib/record/option-rules.js.map +1 -1
- package/src/lib/record/option-rules.mjs +9 -3
- package/src/lib/record/option-rules.mjs.map +1 -1
- package/src/lib/record/record-words.d.mts +13 -1
- package/src/lib/record/record-words.d.ts +13 -1
- package/src/lib/record/record-words.js +29 -1
- package/src/lib/record/record-words.js.map +1 -1
- package/src/lib/record/record-words.mjs +27 -1
- package/src/lib/record/record-words.mjs.map +1 -1
- package/src/lib/record/uploads.d.mts +35 -9
- package/src/lib/record/uploads.d.ts +35 -9
- package/src/lib/record/uploads.js +49 -169
- package/src/lib/record/uploads.js.map +1 -1
- package/src/lib/record/uploads.mjs +42 -164
- package/src/lib/record/uploads.mjs.map +1 -1
- package/src/lib/record/values.d.mts +23 -0
- package/src/lib/record/values.d.ts +23 -0
- package/src/lib/record/values.js +4 -0
- package/src/lib/record/values.js.map +1 -1
- package/src/lib/record/values.mjs +4 -0
- package/src/lib/record/values.mjs.map +1 -1
- package/src/lib/record/waits.d.mts +7 -5
- package/src/lib/record/waits.d.ts +7 -5
- package/src/lib/record/waits.js +7 -10
- package/src/lib/record/waits.js.map +1 -1
- package/src/lib/record/waits.mjs +7 -10
- package/src/lib/record/waits.mjs.map +1 -1
- package/src/runtime.d.mts +13 -6
- package/src/runtime.d.ts +13 -6
- package/src/runtime.js +22 -3
- package/src/runtime.js.map +1 -1
- package/src/runtime.mjs +11 -2
- package/src/runtime.mjs.map +1 -1
package/src/lib/record/form.mjs
CHANGED
|
@@ -21,6 +21,8 @@ import { recordSentence } from './record-words.mjs';
|
|
|
21
21
|
import { askKey, cellOf, listedColumns, notOffered } from './line-choices.mjs';
|
|
22
22
|
import { loadedRecordOf, saveProblemOf, } from './data-source.mjs';
|
|
23
23
|
import { formLimitsPart, hasLimits } from './taking.mjs';
|
|
24
|
+
import { formLanguagesPart, hasLanguages } from './languages.mjs';
|
|
25
|
+
import { formApprovalsPart } from './approving.mjs';
|
|
24
26
|
import { draftRead, resumeStep } from './drafts.mjs';
|
|
25
27
|
import { waitingOn, workedOutFrom } from './waits.mjs';
|
|
26
28
|
import { createUploads, fileBytesIn, uploadGoing, withoutFileBytes } from './uploads.mjs';
|
|
@@ -85,7 +87,9 @@ function innerForm(options, within, place, outer) {
|
|
|
85
87
|
const page = zonedPage(options.page);
|
|
86
88
|
const scheduler = options.scheduler ?? hostScheduler;
|
|
87
89
|
// A field's label small inside a message's sentence where the page asks (`lowerLabels`).
|
|
88
|
-
|
|
90
|
+
// …and a day written in the page's order where it says one (`look.dateOrder`): "can't be after Oct 20, 2026".
|
|
91
|
+
const dateOrder = page.look?.dateOrder;
|
|
92
|
+
const messages = page.lowerLabels || dateOrder ? { ...(options.messages ?? MESSAGES.en), lowerLabels: page.lowerLabels, dateOrder } : (options.messages ?? MESSAGES.en);
|
|
89
93
|
/** One of the form's sentences in the words the page names its records by (`records`), else as the language says it plainly. */
|
|
90
94
|
const named = (noun, plain) => recordSentence(page.records, noun, plain, messages.locale);
|
|
91
95
|
/** The app's functions the page's expressions call: a name none may call is refused now. */
|
|
@@ -359,6 +363,7 @@ function innerForm(options, within, place, outer) {
|
|
|
359
363
|
// Worked out once the form has its state, as the limits part comes (`limitsCode`).
|
|
360
364
|
limits: null,
|
|
361
365
|
resume: null,
|
|
366
|
+
approval: null,
|
|
362
367
|
};
|
|
363
368
|
started = true;
|
|
364
369
|
/**
|
|
@@ -550,6 +555,7 @@ function innerForm(options, within, place, outer) {
|
|
|
550
555
|
let edit = false;
|
|
551
556
|
function set(patch) {
|
|
552
557
|
const was = state.values;
|
|
558
|
+
const choicesWas = state.choices;
|
|
553
559
|
const shown = state.errors;
|
|
554
560
|
const edited = edit;
|
|
555
561
|
edit = false;
|
|
@@ -558,6 +564,12 @@ function innerForm(options, within, place, outer) {
|
|
|
558
564
|
// A line's choices go with their line.
|
|
559
565
|
if (state.values !== was)
|
|
560
566
|
state = { ...state, choices: choicesNow(state.choices) };
|
|
567
|
+
// What was said taken out is said while it is true: an option offered again, or picked again, is said no more.
|
|
568
|
+
if ((state.values !== was || state.choices !== choicesWas) && Object.keys(state.takenOut).length) {
|
|
569
|
+
const takenOut = takenOutTrue();
|
|
570
|
+
if (takenOut !== state.takenOut)
|
|
571
|
+
state = { ...state, takenOut };
|
|
572
|
+
}
|
|
561
573
|
// What waits for an app's answer, as the values shown last settled.
|
|
562
574
|
if (patch.values !== undefined && patch.asked === undefined && !sameAsked(state.asked, askedNow))
|
|
563
575
|
state = { ...state, asked: Object.fromEntries(askedNow) };
|
|
@@ -858,7 +870,9 @@ function innerForm(options, within, place, outer) {
|
|
|
858
870
|
const written = line
|
|
859
871
|
? (values[field] ?? []).map((l) => (l.key === line.key ? { ...l, values: { ...l.values, [line.column]: without(l.values[line.column]) } } : l))
|
|
860
872
|
: without(values[field]);
|
|
861
|
-
|
|
873
|
+
const path = line ? `${field}.${line.key}.${line.column}` : field;
|
|
874
|
+
takenOutOf.set(path, lost.map((value, i) => ({ value, label: labels[i] })));
|
|
875
|
+
writeValues({ ...values, [field]: written }, {}, false, { [path]: takenOutWords(labels) });
|
|
862
876
|
afterEdit(field);
|
|
863
877
|
changed(field, 'step', { keep: true });
|
|
864
878
|
}
|
|
@@ -946,6 +960,10 @@ function innerForm(options, within, place, outer) {
|
|
|
946
960
|
}
|
|
947
961
|
if (hasOptionRules(page))
|
|
948
962
|
void track(optionsCode());
|
|
963
|
+
// A text in several languages is checked by the languages part: asked for at once, a save waiting for it.
|
|
964
|
+
const languagesCode = () => formLanguagesPart.load().then(() => undefined, () => undefined);
|
|
965
|
+
if (hasLanguages(page))
|
|
966
|
+
void track(languagesCode());
|
|
949
967
|
/** 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. */
|
|
950
968
|
function lineOffered(lines, line, column, record) {
|
|
951
969
|
if (optionKit)
|
|
@@ -967,6 +985,38 @@ function innerForm(options, within, place, outer) {
|
|
|
967
985
|
* "Garden is no longer offered, so it was taken out".
|
|
968
986
|
*/
|
|
969
987
|
const takenOutWords = (labels) => fillMessage(messages, labels.length > 1 ? messages.notOfferedNowMany : messages.notOfferedNow, { name: listedLabels(labels.map((label) => (label.includes('{') ? wordsNow(label) : label))) });
|
|
988
|
+
/** The options each note of `takenOut` names, by its path: a note goes once none of them is still out. */
|
|
989
|
+
const takenOutOf = new Map();
|
|
990
|
+
/**
|
|
991
|
+
* The notes of options taken out (`FormState.takenOut`) that are still true for the values and the choices now: one
|
|
992
|
+
* whose options are all offered again — the type that offers them picked again, the treatment ticked again above, the
|
|
993
|
+
* app's list asked again offering it — or picked again goes; one with some still out names those. A list being asked
|
|
994
|
+
* again says nothing yet. The same notes, when none changed.
|
|
995
|
+
*/
|
|
996
|
+
function takenOutTrue() {
|
|
997
|
+
let kept = null;
|
|
998
|
+
for (const path of Object.keys(state.takenOut)) {
|
|
999
|
+
const options = takenOutOf.get(path);
|
|
1000
|
+
const held = valueAt(state.values, path);
|
|
1001
|
+
if (!options || held === gone || state.choices[path]?.loading)
|
|
1002
|
+
continue;
|
|
1003
|
+
const offered = form.offered(path);
|
|
1004
|
+
if (!offered)
|
|
1005
|
+
continue;
|
|
1006
|
+
const chosen = [held].flat();
|
|
1007
|
+
const left = options.filter((option) => !chosen.includes(option.value) && !offered.some((one) => one.value === option.value));
|
|
1008
|
+
if (left.length === options.length)
|
|
1009
|
+
continue;
|
|
1010
|
+
kept ??= { ...state.takenOut };
|
|
1011
|
+
if (left.length) {
|
|
1012
|
+
takenOutOf.set(path, left);
|
|
1013
|
+
kept[path] = takenOutWords(left.map((option) => option.label));
|
|
1014
|
+
}
|
|
1015
|
+
else
|
|
1016
|
+
delete kept[path];
|
|
1017
|
+
}
|
|
1018
|
+
return kept ?? state.takenOut;
|
|
1019
|
+
}
|
|
970
1020
|
/** A line's field as its value is checked: its choices from the app's list, those loaded for its line. */
|
|
971
1021
|
function checkedLine(path, def) {
|
|
972
1022
|
const loaded = def.type === 'selection' && optionsList(def.optionsFrom) ? state.choices[path]?.options : null;
|
|
@@ -1081,6 +1131,9 @@ function innerForm(options, within, place, outer) {
|
|
|
1081
1131
|
const answer = answerOf(node);
|
|
1082
1132
|
if (answer !== undefined && signedNow().has(answer))
|
|
1083
1133
|
readonly = true;
|
|
1134
|
+
// A record waiting for approval: locked but for the fields the page leaves open (the approvals part).
|
|
1135
|
+
if (answer !== undefined && approvalKit?.locks(answer))
|
|
1136
|
+
readonly = true;
|
|
1084
1137
|
return {
|
|
1085
1138
|
invisible,
|
|
1086
1139
|
readonly: readonly || (def !== undefined && lockedField(def)),
|
|
@@ -1152,6 +1205,23 @@ function innerForm(options, within, place, outer) {
|
|
|
1152
1205
|
const name = node.field;
|
|
1153
1206
|
return unticked(node) ?? checkInput(page.fields[name], node.source, state.values[name], messages) ?? checkValue(checked(name), state.values[name], nodeState(node.id).required, messages, today(), node.source.widget) ?? ruleCheck(node).error;
|
|
1154
1207
|
}
|
|
1208
|
+
/** What a check would refuse now (`collectErrors`), for the state it was found for. */
|
|
1209
|
+
let refusals = null;
|
|
1210
|
+
/**
|
|
1211
|
+
* Whether the form refuses the answer at a path — a field's, or a table line's cell (`field.lineKey.subfield`) — as a
|
|
1212
|
+
* check would now: one it shows that holds a value its rules do not take (under its least, past today, a pattern not
|
|
1213
|
+
* met, an answer rule's error). An empty answer is none, required or not: there is nothing worked out from it to wait.
|
|
1214
|
+
*/
|
|
1215
|
+
function refusedAt(path) {
|
|
1216
|
+
if (refusals?.state !== state)
|
|
1217
|
+
refusals = { state, errors: collectErrors() };
|
|
1218
|
+
if (!refusals.errors[path])
|
|
1219
|
+
return false;
|
|
1220
|
+
const parts = path.split('.');
|
|
1221
|
+
const def = page.fields[parts[0]];
|
|
1222
|
+
const held = valueAt(state.values, path);
|
|
1223
|
+
return held !== gone && !isEmpty((parts.length > 2 && def?.type === 'one2many' ? def.fields[parts[parts.length - 1]] : def), held);
|
|
1224
|
+
}
|
|
1155
1225
|
/** Warnings for the visible fields of the page, without publishing them. */
|
|
1156
1226
|
function collectWarnings() {
|
|
1157
1227
|
const warnings = {};
|
|
@@ -1420,6 +1490,8 @@ function innerForm(options, within, place, outer) {
|
|
|
1420
1490
|
if (out) {
|
|
1421
1491
|
settled = out.values;
|
|
1422
1492
|
takenOut = { ...takenOut, ...out.said };
|
|
1493
|
+
for (const [path, gone] of Object.entries(out.gone))
|
|
1494
|
+
takenOutOf.set(path, gone);
|
|
1423
1495
|
}
|
|
1424
1496
|
if (Object.keys(takenOut).length || Object.keys(state.takenOut).length)
|
|
1425
1497
|
extra = { takenOut, ...extra };
|
|
@@ -1700,6 +1772,15 @@ function innerForm(options, within, place, outer) {
|
|
|
1700
1772
|
}
|
|
1701
1773
|
/** A choice of a full option, picked by the person, refused under its field: the field keeps what it held. */
|
|
1702
1774
|
const refusedFull = (name, value) => !!limitsKit?.refusedFull(name, value);
|
|
1775
|
+
// ---- a record's approvals (the approvals part: form-approvals.ts) ---------------------------------------------------
|
|
1776
|
+
/** The approvals made for this form from the approvals part, once it came: none before, nor on a page without them. */
|
|
1777
|
+
let approvalKit = null;
|
|
1778
|
+
/** Whether the form asks for approvals: a page with `approval` (a record's own, as its check says), its source telling them. */
|
|
1779
|
+
const keepsApprovals = !outer && !!page.approval && !!options.dataSource?.approvals;
|
|
1780
|
+
/** The approvals, made from their part once it came; null without them, or where it could not come. */
|
|
1781
|
+
const approvalsCode = () => keepsApprovals ? formApprovalsPart.load().then((part) => (approvalKit ??= part.formApprovals({ form, dataSource: options.dataSource, messages, set, reload: () => load(true) })), () => null) : Promise.resolve(null);
|
|
1782
|
+
/** Ask the record shown's approvals, once their part came; nothing on a page without them. */
|
|
1783
|
+
const askApprovals = () => approvalsCode().then((kit) => kit?.ask());
|
|
1703
1784
|
function scheduleAutosave() {
|
|
1704
1785
|
const autosave = options.autosave;
|
|
1705
1786
|
// A record the person may not change is never saved unasked: it would only be refused.
|
|
@@ -1846,6 +1927,12 @@ function innerForm(options, within, place, outer) {
|
|
|
1846
1927
|
if (asked !== generation)
|
|
1847
1928
|
return elsewhere();
|
|
1848
1929
|
}
|
|
1930
|
+
// A text in several languages, checked in each once its part has come.
|
|
1931
|
+
if (!formLanguagesPart.loaded && hasLanguages(page)) {
|
|
1932
|
+
await languagesCode();
|
|
1933
|
+
if (asked !== generation)
|
|
1934
|
+
return elsewhere();
|
|
1935
|
+
}
|
|
1849
1936
|
// Values the app's functions are asked for are waited for — those that failed asked again — so what is saved is worked out.
|
|
1850
1937
|
if (asking.size || Object.values(state.asked).some((found) => found.failed !== undefined)) {
|
|
1851
1938
|
set({ status: 'saving', error: null, saveProblem: null });
|
|
@@ -1969,6 +2056,9 @@ function innerForm(options, within, place, outer) {
|
|
|
1969
2056
|
scheduleDraft();
|
|
1970
2057
|
scheduleAutosave();
|
|
1971
2058
|
}
|
|
2059
|
+
// A save may have sent it for approval, or moved it on: its approvals asked again.
|
|
2060
|
+
if (keepsApprovals)
|
|
2061
|
+
void track(askApprovals());
|
|
1972
2062
|
// The record as this save left it: a change made while it was on its way is no value of it.
|
|
1973
2063
|
emit('save', { recordId: state.recordId, values: baseline });
|
|
1974
2064
|
void track(moment('on.afterSave', page.on?.afterSave, chain, false));
|
|
@@ -2175,7 +2265,7 @@ function innerForm(options, within, place, outer) {
|
|
|
2175
2265
|
throw new Error(`"${name}" cannot make records: its data source has no create, or it is not a link`);
|
|
2176
2266
|
return track(source.create({ model, name: typed.trim(), ...(Object.keys(values).length ? { values } : {}) }));
|
|
2177
2267
|
}
|
|
2178
|
-
async function runSearch(def, name, ctx, query, limit, { model, ids, exact } = {}) {
|
|
2268
|
+
async function runSearch(def, name, ctx, query, limit, { model, ids, exact, field } = {}) {
|
|
2179
2269
|
// Formatted text names people and records in place (@mention, #4127): searched by the model they are of.
|
|
2180
2270
|
const byModel = def.type === 'reference' || def.type === 'json' || def.type === 'html';
|
|
2181
2271
|
if (def.type !== 'many2one' && def.type !== 'many2many' && !byModel) {
|
|
@@ -2188,9 +2278,11 @@ function innerForm(options, within, place, outer) {
|
|
|
2188
2278
|
return [];
|
|
2189
2279
|
// Every valueFrom, in groups too, becomes the value it names before the search goes out.
|
|
2190
2280
|
const own = byModel || !def.filter ? undefined : resolveFilter(def.filter, ctx);
|
|
2191
|
-
const
|
|
2281
|
+
const kept = ids ? [...(own ?? []), { field: 'id', op: 'in', value: ids }] : own;
|
|
2282
|
+
// A code read by a scanner: the records whose field holds it, whatever their names.
|
|
2283
|
+
const filter = field ? [...(kept ?? []), { field, op: '=', value: query }] : kept;
|
|
2192
2284
|
const relation = byModel ? model : def.relation;
|
|
2193
|
-
return track(source.search({ model: relation, query, ...(filter ? { filter } : {}), limit, ...(exact ? { exact: true } : {}) }));
|
|
2285
|
+
return track(source.search({ model: relation, query: field ? '' : query, ...(filter ? { filter } : {}), limit, ...(exact ? { exact: true } : {}) }));
|
|
2194
2286
|
}
|
|
2195
2287
|
function lineField(field) {
|
|
2196
2288
|
const def = fieldDef(field);
|
|
@@ -2231,6 +2323,9 @@ function innerForm(options, within, place, outer) {
|
|
|
2231
2323
|
loadedOnce = true;
|
|
2232
2324
|
if (asksLimits())
|
|
2233
2325
|
void track(askLimits());
|
|
2326
|
+
// Its approvals: where it stands, what was decided, what this person may decide — its fields locked while it waits.
|
|
2327
|
+
if (keepsApprovals)
|
|
2328
|
+
void track(askApprovals());
|
|
2234
2329
|
if (again)
|
|
2235
2330
|
return;
|
|
2236
2331
|
offerDraft();
|
|
@@ -2263,7 +2358,7 @@ function innerForm(options, within, place, outer) {
|
|
|
2263
2358
|
found = null;
|
|
2264
2359
|
serverKey = null;
|
|
2265
2360
|
draftEpoch++;
|
|
2266
|
-
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 };
|
|
2361
|
+
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 };
|
|
2267
2362
|
if (id != null) {
|
|
2268
2363
|
set({ ...fresh, recordId: id, status: 'idle' });
|
|
2269
2364
|
await load();
|
|
@@ -2682,7 +2777,7 @@ function innerForm(options, within, place, outer) {
|
|
|
2682
2777
|
// Nothing of the record changed; what shows of it may have.
|
|
2683
2778
|
set({});
|
|
2684
2779
|
},
|
|
2685
|
-
fieldReadonly: (name) => lockedField(fieldDef(name)) || state.can?.edit === false || state.can?.readonlyFields?.includes(name) === true || signedNow().has(name),
|
|
2780
|
+
fieldReadonly: (name) => lockedField(fieldDef(name)) || state.can?.edit === false || state.can?.readonlyFields?.includes(name) === true || signedNow().has(name) || !!approvalKit?.locks(name),
|
|
2686
2781
|
loadChoices: (field) => loadChoices(field),
|
|
2687
2782
|
loadDefinitions,
|
|
2688
2783
|
addDefinition,
|
|
@@ -2707,11 +2802,13 @@ function innerForm(options, within, place, outer) {
|
|
|
2707
2802
|
return changesOptions(cell ? (lines?.type === 'one2many' ? lines.fields[cell.column] : undefined) : page.fields[name]);
|
|
2708
2803
|
},
|
|
2709
2804
|
waitsFor(name) {
|
|
2710
|
-
|
|
2711
|
-
if (found)
|
|
2712
|
-
return { field: name, asking: true, ...(found.failed !== undefined ? { failed: found.failed } : {}) };
|
|
2805
|
+
// What it is worked out from first: an answer that does not read, or that the form refuses, is never asked about.
|
|
2713
2806
|
const from = workedOut.get(name);
|
|
2714
|
-
|
|
2807
|
+
const waits = from ? waitingOn(page, from, state.values, state.unread, refusedAt) : null;
|
|
2808
|
+
if (waits)
|
|
2809
|
+
return waits;
|
|
2810
|
+
const found = state.asked[name];
|
|
2811
|
+
return found ? { field: name, asking: true, ...(found.failed !== undefined ? { failed: found.failed } : {}) } : null;
|
|
2715
2812
|
},
|
|
2716
2813
|
problem(name) {
|
|
2717
2814
|
fieldDef(name);
|
|
@@ -3178,6 +3275,14 @@ function innerForm(options, within, place, outer) {
|
|
|
3178
3275
|
return saved;
|
|
3179
3276
|
},
|
|
3180
3277
|
refreshLimits: () => askLimits(),
|
|
3278
|
+
refreshApprovals: askApprovals,
|
|
3279
|
+
async decide(level, decision, reason) {
|
|
3280
|
+
const kit = await approvalsCode();
|
|
3281
|
+
if (!kit)
|
|
3282
|
+
throw new Error('No approvals on this page');
|
|
3283
|
+
return track(kit.decide(level, decision, reason));
|
|
3284
|
+
},
|
|
3285
|
+
approvalLock: (name) => approvalKit?.lockWords(name) ?? null,
|
|
3181
3286
|
async reloadTheirs() {
|
|
3182
3287
|
// A save on its way first: its answer is not dropped by the load.
|
|
3183
3288
|
const asked = shownNow();
|
|
@@ -3362,6 +3467,9 @@ function innerForm(options, within, place, outer) {
|
|
|
3362
3467
|
// choice's places as the source tells them: a record's own once it is loaded, its own choices not counted against it.
|
|
3363
3468
|
if (keepsLimits())
|
|
3364
3469
|
void track(limitsCode());
|
|
3470
|
+
// A record's approvals: their part asked for as the form is made, ready by the time its record is loaded.
|
|
3471
|
+
if (keepsApprovals)
|
|
3472
|
+
void track(approvalsCode());
|
|
3365
3473
|
// A new form has its values now — a draft continued from its key once it is laid — and opens once whoever made it
|
|
3366
3474
|
// has had the chance to listen.
|
|
3367
3475
|
if (state.recordId == null) {
|