@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.d.ts
CHANGED
|
@@ -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 {
|
|
@@ -368,8 +380,8 @@ export interface FormOptions {
|
|
|
368
380
|
};
|
|
369
381
|
/**
|
|
370
382
|
* How a field's value reads inside the page's own words — a `{field}` in a label, a help, a choice, a step's title, a
|
|
371
|
-
* button, an ending — and so inside the form's messages that name a label ("How was your visit to
|
|
372
|
-
*
|
|
383
|
+
* button, an ending — and so inside the form's messages that name a label ("Please answer “How was your visit to
|
|
384
|
+
* Zamalek?”"). The viewer gives its own, as each field shows its value at rest: a choice by its label, money with its
|
|
373
385
|
* currency, a day in the page's order. Left out, a choice by its label, a link by its name, a yes or no as Yes or No,
|
|
374
386
|
* anything else as it is held.
|
|
375
387
|
*/
|
|
@@ -429,11 +441,13 @@ export interface Form {
|
|
|
429
441
|
/** What a field would be told if the form were checked now, or null when it passes. Nothing is shown. */
|
|
430
442
|
problem(field: string): string | null;
|
|
431
443
|
/**
|
|
432
|
-
* What a value worked out from others waits for while one of them holds text that does not read (`unread`)
|
|
433
|
-
*
|
|
434
|
-
*
|
|
435
|
-
*
|
|
436
|
-
*
|
|
444
|
+
* What a value worked out from others waits for while one of them holds text that does not read (`unread`), or an
|
|
445
|
+
* answer the form refuses (shown, not empty, and refused by its checks: an area under its least, a day past today):
|
|
446
|
+
* a total of the page's own (`compute`) over a table whose line says "abc" in its Quantity, a price per m² over an
|
|
447
|
+
* area of −50, or an amount the app works out as the lines change (read-only, from a source with an onchange) — the
|
|
448
|
+
* line, as people count them, and its cell. Shown, it is a dash said in words, as the table's own total waits: never
|
|
449
|
+
* an amount worked out from the value that text replaced, or from one refused. Then what is asked of the app's
|
|
450
|
+
* server for it (`asking`). Null when it shows its value.
|
|
437
451
|
*/
|
|
438
452
|
waitsFor(field: string): WaitsFor | null;
|
|
439
453
|
/**
|
|
@@ -693,6 +707,23 @@ export interface Form {
|
|
|
693
707
|
* show what it tells (`limits`). The form asks it itself as it opens, before its answers go, and after they went.
|
|
694
708
|
*/
|
|
695
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;
|
|
696
727
|
/**
|
|
697
728
|
* Whether a file picked for a file or picture field is stored at once,
|
|
698
729
|
* through the data source's `upload`. False without one: a file then
|
|
@@ -772,6 +803,8 @@ export interface Form {
|
|
|
772
803
|
* makes each ready first, one at a time (a photo made smaller). A field of
|
|
773
804
|
* one file keeps the last one picked. Gives each upload's id. A save waits
|
|
774
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.
|
|
775
808
|
*/
|
|
776
809
|
upload(field: string, files: readonly UploadFile[], how?: UploadHow): string[];
|
|
777
810
|
/**
|
package/src/lib/record/form.js
CHANGED
|
@@ -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");
|
|
@@ -91,7 +93,9 @@ function innerForm(options, within, place, outer) {
|
|
|
91
93
|
const page = (0, zoned_1.zonedPage)(options.page);
|
|
92
94
|
const scheduler = options.scheduler ?? scheduler_1.hostScheduler;
|
|
93
95
|
// A field's label small inside a message's sentence where the page asks (`lowerLabels`).
|
|
94
|
-
|
|
96
|
+
// …and a day written in the page's order where it says one (`look.dateOrder`): "can't be after Oct 20, 2026".
|
|
97
|
+
const dateOrder = page.look?.dateOrder;
|
|
98
|
+
const messages = page.lowerLabels || dateOrder ? { ...(options.messages ?? messages_1.MESSAGES.en), lowerLabels: page.lowerLabels, dateOrder } : (options.messages ?? messages_1.MESSAGES.en);
|
|
95
99
|
/** One of the form's sentences in the words the page names its records by (`records`), else as the language says it plainly. */
|
|
96
100
|
const named = (noun, plain) => (0, record_words_1.recordSentence)(page.records, noun, plain, messages.locale);
|
|
97
101
|
/** The app's functions the page's expressions call: a name none may call is refused now. */
|
|
@@ -365,6 +369,7 @@ function innerForm(options, within, place, outer) {
|
|
|
365
369
|
// Worked out once the form has its state, as the limits part comes (`limitsCode`).
|
|
366
370
|
limits: null,
|
|
367
371
|
resume: null,
|
|
372
|
+
approval: null,
|
|
368
373
|
};
|
|
369
374
|
started = true;
|
|
370
375
|
/**
|
|
@@ -556,6 +561,7 @@ function innerForm(options, within, place, outer) {
|
|
|
556
561
|
let edit = false;
|
|
557
562
|
function set(patch) {
|
|
558
563
|
const was = state.values;
|
|
564
|
+
const choicesWas = state.choices;
|
|
559
565
|
const shown = state.errors;
|
|
560
566
|
const edited = edit;
|
|
561
567
|
edit = false;
|
|
@@ -564,6 +570,12 @@ function innerForm(options, within, place, outer) {
|
|
|
564
570
|
// A line's choices go with their line.
|
|
565
571
|
if (state.values !== was)
|
|
566
572
|
state = { ...state, choices: choicesNow(state.choices) };
|
|
573
|
+
// What was said taken out is said while it is true: an option offered again, or picked again, is said no more.
|
|
574
|
+
if ((state.values !== was || state.choices !== choicesWas) && Object.keys(state.takenOut).length) {
|
|
575
|
+
const takenOut = takenOutTrue();
|
|
576
|
+
if (takenOut !== state.takenOut)
|
|
577
|
+
state = { ...state, takenOut };
|
|
578
|
+
}
|
|
567
579
|
// What waits for an app's answer, as the values shown last settled.
|
|
568
580
|
if (patch.values !== undefined && patch.asked === undefined && !sameAsked(state.asked, askedNow))
|
|
569
581
|
state = { ...state, asked: Object.fromEntries(askedNow) };
|
|
@@ -864,7 +876,9 @@ function innerForm(options, within, place, outer) {
|
|
|
864
876
|
const written = line
|
|
865
877
|
? (values[field] ?? []).map((l) => (l.key === line.key ? { ...l, values: { ...l.values, [line.column]: without(l.values[line.column]) } } : l))
|
|
866
878
|
: without(values[field]);
|
|
867
|
-
|
|
879
|
+
const path = line ? `${field}.${line.key}.${line.column}` : field;
|
|
880
|
+
takenOutOf.set(path, lost.map((value, i) => ({ value, label: labels[i] })));
|
|
881
|
+
writeValues({ ...values, [field]: written }, {}, false, { [path]: takenOutWords(labels) });
|
|
868
882
|
afterEdit(field);
|
|
869
883
|
changed(field, 'step', { keep: true });
|
|
870
884
|
}
|
|
@@ -952,6 +966,10 @@ function innerForm(options, within, place, outer) {
|
|
|
952
966
|
}
|
|
953
967
|
if ((0, choice_options_1.hasOptionRules)(page))
|
|
954
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());
|
|
955
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. */
|
|
956
974
|
function lineOffered(lines, line, column, record) {
|
|
957
975
|
if (optionKit)
|
|
@@ -973,6 +991,38 @@ function innerForm(options, within, place, outer) {
|
|
|
973
991
|
* "Garden is no longer offered, so it was taken out".
|
|
974
992
|
*/
|
|
975
993
|
const takenOutWords = (labels) => (0, messages_1.fillMessage)(messages, labels.length > 1 ? messages.notOfferedNowMany : messages.notOfferedNow, { name: listedLabels(labels.map((label) => (label.includes('{') ? wordsNow(label) : label))) });
|
|
994
|
+
/** The options each note of `takenOut` names, by its path: a note goes once none of them is still out. */
|
|
995
|
+
const takenOutOf = new Map();
|
|
996
|
+
/**
|
|
997
|
+
* The notes of options taken out (`FormState.takenOut`) that are still true for the values and the choices now: one
|
|
998
|
+
* whose options are all offered again — the type that offers them picked again, the treatment ticked again above, the
|
|
999
|
+
* app's list asked again offering it — or picked again goes; one with some still out names those. A list being asked
|
|
1000
|
+
* again says nothing yet. The same notes, when none changed.
|
|
1001
|
+
*/
|
|
1002
|
+
function takenOutTrue() {
|
|
1003
|
+
let kept = null;
|
|
1004
|
+
for (const path of Object.keys(state.takenOut)) {
|
|
1005
|
+
const options = takenOutOf.get(path);
|
|
1006
|
+
const held = valueAt(state.values, path);
|
|
1007
|
+
if (!options || held === gone || state.choices[path]?.loading)
|
|
1008
|
+
continue;
|
|
1009
|
+
const offered = form.offered(path);
|
|
1010
|
+
if (!offered)
|
|
1011
|
+
continue;
|
|
1012
|
+
const chosen = [held].flat();
|
|
1013
|
+
const left = options.filter((option) => !chosen.includes(option.value) && !offered.some((one) => one.value === option.value));
|
|
1014
|
+
if (left.length === options.length)
|
|
1015
|
+
continue;
|
|
1016
|
+
kept ??= { ...state.takenOut };
|
|
1017
|
+
if (left.length) {
|
|
1018
|
+
takenOutOf.set(path, left);
|
|
1019
|
+
kept[path] = takenOutWords(left.map((option) => option.label));
|
|
1020
|
+
}
|
|
1021
|
+
else
|
|
1022
|
+
delete kept[path];
|
|
1023
|
+
}
|
|
1024
|
+
return kept ?? state.takenOut;
|
|
1025
|
+
}
|
|
976
1026
|
/** A line's field as its value is checked: its choices from the app's list, those loaded for its line. */
|
|
977
1027
|
function checkedLine(path, def) {
|
|
978
1028
|
const loaded = def.type === 'selection' && (0, choice_options_1.optionsList)(def.optionsFrom) ? state.choices[path]?.options : null;
|
|
@@ -1087,6 +1137,9 @@ function innerForm(options, within, place, outer) {
|
|
|
1087
1137
|
const answer = answerOf(node);
|
|
1088
1138
|
if (answer !== undefined && signedNow().has(answer))
|
|
1089
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;
|
|
1090
1143
|
return {
|
|
1091
1144
|
invisible,
|
|
1092
1145
|
readonly: readonly || (def !== undefined && lockedField(def)),
|
|
@@ -1158,6 +1211,23 @@ function innerForm(options, within, place, outer) {
|
|
|
1158
1211
|
const name = node.field;
|
|
1159
1212
|
return unticked(node) ?? (0, inputs_1.checkInput)(page.fields[name], node.source, state.values[name], messages) ?? (0, check_1.checkValue)(checked(name), state.values[name], nodeState(node.id).required, messages, today(), node.source.widget) ?? ruleCheck(node).error;
|
|
1160
1213
|
}
|
|
1214
|
+
/** What a check would refuse now (`collectErrors`), for the state it was found for. */
|
|
1215
|
+
let refusals = null;
|
|
1216
|
+
/**
|
|
1217
|
+
* Whether the form refuses the answer at a path — a field's, or a table line's cell (`field.lineKey.subfield`) — as a
|
|
1218
|
+
* check would now: one it shows that holds a value its rules do not take (under its least, past today, a pattern not
|
|
1219
|
+
* met, an answer rule's error). An empty answer is none, required or not: there is nothing worked out from it to wait.
|
|
1220
|
+
*/
|
|
1221
|
+
function refusedAt(path) {
|
|
1222
|
+
if (refusals?.state !== state)
|
|
1223
|
+
refusals = { state, errors: collectErrors() };
|
|
1224
|
+
if (!refusals.errors[path])
|
|
1225
|
+
return false;
|
|
1226
|
+
const parts = path.split('.');
|
|
1227
|
+
const def = page.fields[parts[0]];
|
|
1228
|
+
const held = valueAt(state.values, path);
|
|
1229
|
+
return held !== gone && !(0, values_1.isEmpty)((parts.length > 2 && def?.type === 'one2many' ? def.fields[parts[parts.length - 1]] : def), held);
|
|
1230
|
+
}
|
|
1161
1231
|
/** Warnings for the visible fields of the page, without publishing them. */
|
|
1162
1232
|
function collectWarnings() {
|
|
1163
1233
|
const warnings = {};
|
|
@@ -1426,6 +1496,8 @@ function innerForm(options, within, place, outer) {
|
|
|
1426
1496
|
if (out) {
|
|
1427
1497
|
settled = out.values;
|
|
1428
1498
|
takenOut = { ...takenOut, ...out.said };
|
|
1499
|
+
for (const [path, gone] of Object.entries(out.gone))
|
|
1500
|
+
takenOutOf.set(path, gone);
|
|
1429
1501
|
}
|
|
1430
1502
|
if (Object.keys(takenOut).length || Object.keys(state.takenOut).length)
|
|
1431
1503
|
extra = { takenOut, ...extra };
|
|
@@ -1706,6 +1778,15 @@ function innerForm(options, within, place, outer) {
|
|
|
1706
1778
|
}
|
|
1707
1779
|
/** A choice of a full option, picked by the person, refused under its field: the field keeps what it held. */
|
|
1708
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());
|
|
1709
1790
|
function scheduleAutosave() {
|
|
1710
1791
|
const autosave = options.autosave;
|
|
1711
1792
|
// A record the person may not change is never saved unasked: it would only be refused.
|
|
@@ -1852,6 +1933,12 @@ function innerForm(options, within, place, outer) {
|
|
|
1852
1933
|
if (asked !== generation)
|
|
1853
1934
|
return elsewhere();
|
|
1854
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
|
+
}
|
|
1855
1942
|
// Values the app's functions are asked for are waited for — those that failed asked again — so what is saved is worked out.
|
|
1856
1943
|
if (asking.size || Object.values(state.asked).some((found) => found.failed !== undefined)) {
|
|
1857
1944
|
set({ status: 'saving', error: null, saveProblem: null });
|
|
@@ -1975,6 +2062,9 @@ function innerForm(options, within, place, outer) {
|
|
|
1975
2062
|
scheduleDraft();
|
|
1976
2063
|
scheduleAutosave();
|
|
1977
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());
|
|
1978
2068
|
// The record as this save left it: a change made while it was on its way is no value of it.
|
|
1979
2069
|
emit('save', { recordId: state.recordId, values: baseline });
|
|
1980
2070
|
void track(moment('on.afterSave', page.on?.afterSave, chain, false));
|
|
@@ -2181,7 +2271,7 @@ function innerForm(options, within, place, outer) {
|
|
|
2181
2271
|
throw new Error(`"${name}" cannot make records: its data source has no create, or it is not a link`);
|
|
2182
2272
|
return track(source.create({ model, name: typed.trim(), ...(Object.keys(values).length ? { values } : {}) }));
|
|
2183
2273
|
}
|
|
2184
|
-
async function runSearch(def, name, ctx, query, limit, { model, ids, exact } = {}) {
|
|
2274
|
+
async function runSearch(def, name, ctx, query, limit, { model, ids, exact, field } = {}) {
|
|
2185
2275
|
// Formatted text names people and records in place (@mention, #4127): searched by the model they are of.
|
|
2186
2276
|
const byModel = def.type === 'reference' || def.type === 'json' || def.type === 'html';
|
|
2187
2277
|
if (def.type !== 'many2one' && def.type !== 'many2many' && !byModel) {
|
|
@@ -2194,9 +2284,11 @@ function innerForm(options, within, place, outer) {
|
|
|
2194
2284
|
return [];
|
|
2195
2285
|
// Every valueFrom, in groups too, becomes the value it names before the search goes out.
|
|
2196
2286
|
const own = byModel || !def.filter ? undefined : (0, filter_1.resolveFilter)(def.filter, ctx);
|
|
2197
|
-
const
|
|
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;
|
|
2198
2290
|
const relation = byModel ? model : def.relation;
|
|
2199
|
-
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 } : {}) }));
|
|
2200
2292
|
}
|
|
2201
2293
|
function lineField(field) {
|
|
2202
2294
|
const def = fieldDef(field);
|
|
@@ -2237,6 +2329,9 @@ function innerForm(options, within, place, outer) {
|
|
|
2237
2329
|
loadedOnce = true;
|
|
2238
2330
|
if (asksLimits())
|
|
2239
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());
|
|
2240
2335
|
if (again)
|
|
2241
2336
|
return;
|
|
2242
2337
|
offerDraft();
|
|
@@ -2269,7 +2364,7 @@ function innerForm(options, within, place, outer) {
|
|
|
2269
2364
|
found = null;
|
|
2270
2365
|
serverKey = null;
|
|
2271
2366
|
draftEpoch++;
|
|
2272
|
-
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 };
|
|
2273
2368
|
if (id != null) {
|
|
2274
2369
|
set({ ...fresh, recordId: id, status: 'idle' });
|
|
2275
2370
|
await load();
|
|
@@ -2688,7 +2783,7 @@ function innerForm(options, within, place, outer) {
|
|
|
2688
2783
|
// Nothing of the record changed; what shows of it may have.
|
|
2689
2784
|
set({});
|
|
2690
2785
|
},
|
|
2691
|
-
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),
|
|
2692
2787
|
loadChoices: (field) => loadChoices(field),
|
|
2693
2788
|
loadDefinitions,
|
|
2694
2789
|
addDefinition,
|
|
@@ -2713,11 +2808,13 @@ function innerForm(options, within, place, outer) {
|
|
|
2713
2808
|
return (0, choice_options_1.changesOptions)(cell ? (lines?.type === 'one2many' ? lines.fields[cell.column] : undefined) : page.fields[name]);
|
|
2714
2809
|
},
|
|
2715
2810
|
waitsFor(name) {
|
|
2716
|
-
|
|
2717
|
-
if (found)
|
|
2718
|
-
return { field: name, asking: true, ...(found.failed !== undefined ? { failed: found.failed } : {}) };
|
|
2811
|
+
// What it is worked out from first: an answer that does not read, or that the form refuses, is never asked about.
|
|
2719
2812
|
const from = workedOut.get(name);
|
|
2720
|
-
|
|
2813
|
+
const waits = from ? (0, waits_1.waitingOn)(page, from, state.values, state.unread, refusedAt) : null;
|
|
2814
|
+
if (waits)
|
|
2815
|
+
return waits;
|
|
2816
|
+
const found = state.asked[name];
|
|
2817
|
+
return found ? { field: name, asking: true, ...(found.failed !== undefined ? { failed: found.failed } : {}) } : null;
|
|
2721
2818
|
},
|
|
2722
2819
|
problem(name) {
|
|
2723
2820
|
fieldDef(name);
|
|
@@ -3184,6 +3281,14 @@ function innerForm(options, within, place, outer) {
|
|
|
3184
3281
|
return saved;
|
|
3185
3282
|
},
|
|
3186
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,
|
|
3187
3292
|
async reloadTheirs() {
|
|
3188
3293
|
// A save on its way first: its answer is not dropped by the load.
|
|
3189
3294
|
const asked = shownNow();
|
|
@@ -3368,6 +3473,9 @@ function innerForm(options, within, place, outer) {
|
|
|
3368
3473
|
// choice's places as the source tells them: a record's own once it is loaded, its own choices not counted against it.
|
|
3369
3474
|
if (keepsLimits())
|
|
3370
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());
|
|
3371
3479
|
// A new form has its values now — a draft continued from its key once it is laid — and opens once whoever made it
|
|
3372
3480
|
// has had the chance to listen.
|
|
3373
3481
|
if (state.recordId == null) {
|