@esfaenza/flow-builder 20.3.52 → 20.3.54

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.
@@ -965,6 +965,143 @@ function variantPresetOf(type, variant) {
965
965
  return { [field]: variant };
966
966
  }
967
967
 
968
+ /**
969
+ * Il trigger su record, e i **valori precedenti** — FRONTEND.md §3.4, §4.1, §7.
970
+ *
971
+ * Due domande che sembrano una sola e non lo sono. La prima: questo flow parte da un record? La
972
+ * seconda: i **valori di prima** del record ci sono? Non discende dal momento (`triggerType`) ma
973
+ * dall'**operazione** (`recordTriggerType`), ed e' l'unica cosa che decide quanto costa scrivere un
974
+ * `IsChanged` o un `$Record__Prior` — perche' ci sono **sempre** (aggiornamento), **a volte**
975
+ * (creazione e aggiornamento: sulla creazione no) o **mai** (creazione, eliminazione).
976
+ *
977
+ * Da lì le tre gravita' di `PRIOR_VALUES_NOT_AVAILABLE`, che la §7 dichiara e che quindi non si
978
+ * indovinano: dove i valori non ci sono mai l'interview **fallisce** davanti a chi stava salvando —
979
+ * errore; dove ci sono a volte il flow funziona a meta' — avviso; dove ci sono sempre non c'e'
980
+ * niente da dire. La regola sta qui, in un posto solo, e la applicano tutti e due i capi: l'editor,
981
+ * che lo dice mentre si scrive la condizione, e il validatore, che emette il rilievo. Due copie
982
+ * divergono, e la si scopre quando l'editor tace su cio' che il backend rifiuta.
983
+ *
984
+ * L'eccezione e' `doesRequireRecordChangedToMeetCriteria`: il runtime lo **ignora** invece di
985
+ * fallire, quindi non e' mai un errore, e sull'operazione «a volte» non e' nemmeno un rilievo —
986
+ * ignorarlo sulla creazione e' il comportamento giusto, non un difetto.
987
+ *
988
+ * Restano fuori le **formule**: un'espressione che cita `$Record__Prior` non e' analizzabile qui,
989
+ * perche' la sintassi la conosce solo il motore (§6.3).
990
+ */
991
+ /**
992
+ * I trigger che lavorano su un record, cioe' quelli che rendono disponibile `$Record` (§4.1).
993
+ * Il contratto li nomina uno per uno (§3.4): non e' un dizionario da leggere, e' la tabella dei sei
994
+ * tipi di flow.
995
+ */
996
+ const RECORD_TRIGGERS = new Set([
997
+ 'RecordBeforeSave',
998
+ 'RecordAfterSave',
999
+ 'RecordBeforeDelete',
1000
+ ]);
1001
+ /** L'operatore che legge i valori precedenti. Lo nomina la §7: non c'e' un flag di dizionario. */
1002
+ const PRIOR_VALUES_OPERATOR = 'IsChanged';
1003
+ /** Lo scope che **e'** i valori precedenti (§4.1). */
1004
+ const PRIOR_VALUES_SCOPE = '$Record__Prior';
1005
+ function isRecordTrigger(triggerType) {
1006
+ return RECORD_TRIGGERS.has(triggerType ?? '');
1007
+ }
1008
+ /**
1009
+ * §3.4, §7 — la domanda si pone all'**operazione**, non al momento, e le quattro operazioni le
1010
+ * nomina il contratto. Dove l'operazione manca la risposta la da' il trigger, com'e' scritto nella
1011
+ * §3.4: su un trigger di salvataggio l'assenza vale `CreateAndUpdate`, e con `RecordBeforeDelete`
1012
+ * l'operazione la dice il trigger — cioe' un'eliminazione, che un «prima» non ce l'ha.
1013
+ */
1014
+ function priorValuesAvailability(triggerType, operation) {
1015
+ if (!isRecordTrigger(triggerType)) {
1016
+ return 'unknown';
1017
+ }
1018
+ switch (operation) {
1019
+ case 'Update':
1020
+ return 'always';
1021
+ case 'CreateAndUpdate':
1022
+ return 'sometimes';
1023
+ case 'Create':
1024
+ case 'Delete':
1025
+ return 'never';
1026
+ }
1027
+ if (!operation) {
1028
+ return triggerType === 'RecordBeforeDelete' ? 'never' : 'sometimes';
1029
+ }
1030
+ // Un'operazione che il contratto non nomina: non si sa, e non sapere non e' sapere che e'
1031
+ // sbagliato — resta l'avviso, mai l'errore.
1032
+ return 'unknown';
1033
+ }
1034
+ /** La stessa domanda con in mano il documento: la coppia sta in un posto solo, lo Start (§3.4). */
1035
+ function priorValuesOfDefinition(definition) {
1036
+ const start = definition?.start?.[0];
1037
+ return priorValuesAvailability(start?.triggerType, start?.recordTriggerType);
1038
+ }
1039
+ /**
1040
+ * §7 — quanto costa **leggere** i valori precedenti: `IsChanged`, o un riferimento radicato su
1041
+ * `$Record__Prior`. Mai ci sono = a runtime l'interview fallisce, quindi errore; a volte = il flow
1042
+ * funziona a meta', quindi avviso.
1043
+ */
1044
+ function priorValuesSeverity(availability) {
1045
+ switch (availability) {
1046
+ case 'always':
1047
+ return 'none';
1048
+ case 'never':
1049
+ return 'error';
1050
+ default:
1051
+ return 'warning';
1052
+ }
1053
+ }
1054
+ /**
1055
+ * §7 — l'eccezione: `doesRequireRecordChangedToMeetCriteria` il runtime lo **ignora** invece di
1056
+ * fallire, quindi non e' mai un errore; e su `CreateAndUpdate` non e' nemmeno un rilievo, perche'
1057
+ * ignorarlo sulla creazione e' cio' che deve accadere.
1058
+ */
1059
+ function priorCriteriaSeverity(availability) {
1060
+ return availability === 'never' ? 'warning' : 'none';
1061
+ }
1062
+ /**
1063
+ * §6.3 — se ha senso **proporre** cio' che legge i valori precedenti: una funzione di formula
1064
+ * marcata `requiresPriorValues`, in una palette.
1065
+ *
1066
+ * Non e' {@link priorValuesSeverity} con altro nome, e la differenza sta tutta nel caso «non lo
1067
+ * so»: un flow che non e' innescato da un record lì e' un **avviso** — i valori puo' passarli chi
1068
+ * avvia l'interview, accusarlo sarebbe un falso positivo — mentre qui e' un motivo per non mettere
1069
+ * la voce in elenco, perche' proporre `PRIORVALUE` dove non c'e' nessun record che innesca e'
1070
+ * proporre una funzione che non ha su cosa lavorare. Su «creazione e aggiornamento» si propone: la
1071
+ * meta' aggiornamento la usa.
1072
+ *
1073
+ * Resta una **proposta**: chi la scrive a mano non viene accusato da qui, ma da `validateFormula`.
1074
+ */
1075
+ function priorValuesProposable(availability) {
1076
+ return availability === 'always' || availability === 'sometimes';
1077
+ }
1078
+ /** `true` se il riferimento e' radicato sui valori precedenti: e' la **radice** che conta (§4.1). */
1079
+ function readsPriorValues(reference) {
1080
+ const trimmed = (reference ?? '').trim();
1081
+ if (!trimmed) {
1082
+ return false;
1083
+ }
1084
+ const dot = trimmed.indexOf('.');
1085
+ const root = dot < 0 ? trimmed : trimmed.slice(0, dot);
1086
+ return root.toLowerCase() === PRIOR_VALUES_SCOPE.toLowerCase();
1087
+ }
1088
+ /**
1089
+ * Perche' i valori precedenti mancano, in una frase — la stessa da tutte e due le parti, cosi' chi
1090
+ * la legge nell'editor e chi la rilegge nel pannello dei problemi non trova due spiegazioni diverse.
1091
+ */
1092
+ function describePriorValues(availability) {
1093
+ switch (availability) {
1094
+ case 'never':
1095
+ return 'con questa operazione il record di prima non esiste';
1096
+ case 'sometimes':
1097
+ return 'sulla creazione il record di prima non esiste, e li’ la condizione non e’ valutabile';
1098
+ case 'unknown':
1099
+ return 'questo flow non e’ innescato da un record: i valori precedenti li passa chi avvia l’interview';
1100
+ default:
1101
+ return '';
1102
+ }
1103
+ }
1104
+
968
1105
  /**
969
1106
  * Nomi — FRONTEND.md §3.3, §13.5.
970
1107
  *
@@ -3885,6 +4022,7 @@ class FlowDictionaryStore {
3885
4022
  globalVariables = computed(() => this._dictionaries().globalVariables ?? [], ...(ngDevMode ? [{ debugName: "globalVariables" }] : []));
3886
4023
  processTypes = computed(() => this._dictionaries().processTypes ?? [], ...(ngDevMode ? [{ debugName: "processTypes" }] : []));
3887
4024
  triggerTypes = computed(() => this._dictionaries().triggerTypes ?? [], ...(ngDevMode ? [{ debugName: "triggerTypes" }] : []));
4025
+ /** §3.4 — le quattro operazioni che innescano un flow da un record, coi momenti che ammettono. */
3888
4026
  recordTriggerTypes = computed(() => this._dictionaries().recordTriggerTypes ?? [], ...(ngDevMode ? [{ debugName: "recordTriggerTypes" }] : []));
3889
4027
  // -------------------------------------------------------------------------
3890
4028
  // §3.4 — il tipo di flow
@@ -3924,6 +4062,73 @@ class FlowDictionaryStore {
3924
4062
  flowTypeOfDefinition(definition) {
3925
4063
  return this.flowTypeOf(definition?.processType, definition?.start?.[0]?.triggerType);
3926
4064
  }
4065
+ // -------------------------------------------------------------------------
4066
+ // §3.4 — l'operazione e il momento di un trigger su record
4067
+ // -------------------------------------------------------------------------
4068
+ /**
4069
+ * La voce di un'operazione (`recordTriggerType`). Voce assente = nessun flag, cioe' «non lo so»:
4070
+ * chi legge torna a proporre i momenti del tipo di flow invece di togliere opzioni.
4071
+ */
4072
+ recordTriggerType(value) {
4073
+ return value ? this.recordTriggerTypes().find((entry) => entry.value === value) : undefined;
4074
+ }
4075
+ /**
4076
+ * §3.4 — i **momenti** che un'operazione ammette, ed e' l'unico elenco da cui proporli: quello di
4077
+ * primo livello proporrebbe coppie che la validazione rifiuta (`START_TRIGGER_MISMATCH`).
4078
+ *
4079
+ * Qui sta la normalizzazione delle due forme in cui le voci arrivano — il solo valore oppure la
4080
+ * voce intera — perche' un host puo' serializzarle in entrambi i modi e riconoscerne una sola
4081
+ * significa funzionare con meta' dei dizionari senza che nulla lo dica. Dove arriva il solo
4082
+ * valore, etichetta e descrizione si ripescano dal dizionario `triggerTypes`, che e' dove stanno
4083
+ * comunque; dove non c'e' nemmeno quello resta il valore grezzo, che e' meglio di una riga vuota.
4084
+ *
4085
+ * Elenco vuoto = il dizionario non li dichiara, **non** «nessun momento ammesso».
4086
+ */
4087
+ recordTriggerTimings(value) {
4088
+ const declared = this.recordTriggerType(value)?.triggerTypes ?? [];
4089
+ return declared.map((timing) => {
4090
+ if (typeof timing !== 'string') {
4091
+ return timing;
4092
+ }
4093
+ const known = this.triggerTypes().find((entry) => entry.value === timing);
4094
+ return known ?? { value: timing, label: timing };
4095
+ });
4096
+ }
4097
+ /**
4098
+ * Il momento da scrivere quando non si sceglie — l'eliminazione — o quando quello in mano non e'
4099
+ * ammesso dall'operazione appena scelta. Ripiega sul primo dichiarato: `defaultTriggerType` e'
4100
+ * facoltativo, e un'operazione con un momento solo non ha bisogno di dichiararlo due volte.
4101
+ */
4102
+ defaultTimingOf(value) {
4103
+ const entry = this.recordTriggerType(value);
4104
+ return entry?.defaultTriggerType ?? this.recordTriggerTimings(value)[0]?.value;
4105
+ }
4106
+ /**
4107
+ * La domanda all'incontrario: quali operazioni ammettono questo momento. Serve dove il momento si
4108
+ * sceglie per primo — cioe' solo nel ripiego, su un documento il cui tipo di flow non si
4109
+ * riconosce — per rimettere a posto l'operazione invece di lasciare una coppia impossibile.
4110
+ */
4111
+ recordTriggerTypesFor(trigger) {
4112
+ if (!trigger) {
4113
+ return [];
4114
+ }
4115
+ return this.recordTriggerTypes().filter((entry) => this.recordTriggerTimings(entry.value).some((timing) => timing.value === trigger));
4116
+ }
4117
+ /**
4118
+ * §3.4 — se la coppia operazione + momento e' una delle sei.
4119
+ *
4120
+ * `unknown` e' il caso normale su un dizionario che non dichiara i momenti, e non e' un difetto:
4121
+ * accusare una coppia senza avere la tabella con cui giudicarla e' peggio che tacere. Un editor
4122
+ * che rende la sequenza — l'operazione prima, il momento dopo — `mismatch` non lo produce mai: lo
4123
+ * incontra solo su un documento scritto altrove.
4124
+ */
4125
+ recordTriggerPair(operation, trigger) {
4126
+ const timings = this.recordTriggerTimings(operation);
4127
+ if (!operation || !trigger || !timings.length) {
4128
+ return 'unknown';
4129
+ }
4130
+ return timings.some((timing) => timing.value === trigger) ? 'ok' : 'mismatch';
4131
+ }
3927
4132
  startFrequencies = computed(() => this._dictionaries().startFrequencies ?? [], ...(ngDevMode ? [{ debugName: "startFrequencies" }] : []));
3928
4133
  offsetUnits = computed(() => this._dictionaries().offsetUnits ?? [], ...(ngDevMode ? [{ debugName: "offsetUnits" }] : []));
3929
4134
  sortOrders = computed(() => this._dictionaries().sortOrders ?? [], ...(ngDevMode ? [{ debugName: "sortOrders" }] : []));
@@ -4276,9 +4481,21 @@ class FlowCatalogStore {
4276
4481
  *
4277
4482
  * La primitiva e' opzionale e l'errore diventa lista vuota — che qui significa «il motore non
4278
4483
  * le dichiara»: nessuna palette e nessuna accusa a una funzione fuori elenco (§4.6, §6.3).
4279
- */
4280
- listFormulaFunctions(usage) {
4281
- return this.formulaFunctionsCache.get(usage ?? '', () => this.api.listFormulaFunctions(usage).then((list) => list ?? []).catch(() => []));
4484
+ *
4485
+ * `availability` toglie le funzioni che leggono i **valori precedenti** del record dove un
4486
+ * «prima» non c'e' (§6.3). Il filtro sta **dopo** la cache di proposito: la risposta del motore
4487
+ * e' la stessa per tutto il documento, e cio' che cambia e' l'operazione dello Start — rifetchare
4488
+ * a ogni cambio sarebbe una richiesta per niente, e mettere l'operazione nella chiave terrebbe in
4489
+ * memoria quattro copie dello stesso elenco. Assente = non si filtra: e' «non lo so», la regola
4490
+ * dei cataloghi. Dove il backend ha già filtrato questo non toglie niente — la regola e' la
4491
+ * stessa, applicata ai due capi.
4492
+ */
4493
+ listFormulaFunctions(usage, availability) {
4494
+ return this.formulaFunctionsCache
4495
+ .get(usage ?? '', () => this.api.listFormulaFunctions(usage).then((list) => list ?? []).catch(() => []))
4496
+ .then((list) => !availability || priorValuesProposable(availability)
4497
+ ? list
4498
+ : list.filter((entry) => !entry.requiresPriorValues));
4282
4499
  }
4283
4500
  listEnumTypes() {
4284
4501
  return this.entriesCache.get('enum-types', () => this.api.listEnumTypes().then((list) => list ?? []));
@@ -7491,6 +7708,11 @@ class ReferencePickerComponent {
7491
7708
  * - `globalPathUntyped`: percorso globale che porta un contenitore senza dichiararne il tipo:
7492
7709
  * `PATH_NOT_VERIFIABLE`, e la correzione sta nel catalogo dell'host, non nel flow (§4.1);
7493
7710
  * - `globalPathInvalid`: si naviga un percorso globale scalare o una collection: `GLOBAL_UNKNOWN`;
7711
+ * - `priorValuesMissing`: il valore e' radicato su `$Record__Prior` e i valori precedenti non
7712
+ * ci sono, o non ci sono sempre: `PRIOR_VALUES_NOT_AVAILABLE`, errore o avviso secondo
7713
+ * l'operazione che innesca il flow (§7). Si guarda **prima** di tutto il resto, perche' un
7714
+ * host che smette di proporre quello scope lo farebbe cadere in «questo nome non esiste», che
7715
+ * manda a cercare un nome sbagliato invece di dire che manca il record di prima;
7494
7716
  * - `unknown`: nessuna corrispondenza: si avvisa, senza bloccare. Ci finisce anche il
7495
7717
  * percorso inesistente di uno scope che i percorsi li dichiara, perche' lì il backend
7496
7718
  * risponderebbe `GLOBAL_UNKNOWN`.
@@ -7500,6 +7722,9 @@ class ReferencePickerComponent {
7500
7722
  if (!value) {
7501
7723
  return 'empty';
7502
7724
  }
7725
+ if (readsPriorValues(value) && this.priorValuesSeverity() !== 'none') {
7726
+ return 'priorValuesMissing';
7727
+ }
7503
7728
  const all = this.references();
7504
7729
  if (all.some((reference) => reference.name === value)) {
7505
7730
  return 'known';
@@ -7600,16 +7825,42 @@ class ReferencePickerComponent {
7600
7825
  return `«${this.declaredPrefix()?.name}» porta un contenitore, ma il catalogo non ne dichiara il tipo: il percorso non e’ verificabile (PATH_NOT_VERIFIABLE).`;
7601
7826
  case 'globalPathInvalid':
7602
7827
  return `«${this.declaredPrefix()?.name}» e’ un valore, non un contenitore: dopo il punto non c’e’ niente da navigare (GLOBAL_UNKNOWN).`;
7828
+ case 'priorValuesMissing': {
7829
+ const availability = priorValuesOfDefinition(this.store.document());
7830
+ const consequence = availability === 'never'
7831
+ ? ': a runtime l’interview fallisce'
7832
+ : availability === 'sometimes'
7833
+ ? ': il flow funziona a meta’'
7834
+ : '';
7835
+ return `Questo e’ il record prima del salvataggio, ma ${describePriorValues(availability)}${consequence} (PRIOR_VALUES_NOT_AVAILABLE).`;
7836
+ }
7603
7837
  case 'unknown':
7604
7838
  return 'Questo nome non e’ fra i riferimenti disponibili: la validazione lo segnalerebbe.';
7605
7839
  default:
7606
7840
  return null;
7607
7841
  }
7608
7842
  }, ...(ngDevMode ? [{ debugName: "hint" }] : []));
7843
+ /**
7844
+ * §7 — la gravita' di leggere i valori precedenti in **questo** documento: la decide l'operazione
7845
+ * che innesca il flow, non lo stato del picker, ed e' l'unico stato con due colori possibili.
7846
+ */
7847
+ priorValuesSeverity = computed(() => priorValuesSeverity(priorValuesOfDefinition(this.store.document())), ...(ngDevMode ? [{ debugName: "priorValuesSeverity" }] : []));
7609
7848
  /** Gli stati che sono avvisi: «non lo so», o un valore da correggere che non blocca. */
7610
- isHintWarning = computed(() => ['unknown', 'containerRoot', 'globalPathUntyped'].includes(this.valueState()), ...(ngDevMode ? [{ debugName: "isHintWarning" }] : []));
7849
+ isHintWarning = computed(() => {
7850
+ const state = this.valueState();
7851
+ if (state === 'priorValuesMissing') {
7852
+ return this.priorValuesSeverity() === 'warning';
7853
+ }
7854
+ return ['unknown', 'containerRoot', 'globalPathUntyped'].includes(state);
7855
+ }, ...(ngDevMode ? [{ debugName: "isHintWarning" }] : []));
7611
7856
  /** Gli stati che sono errori e non avvisi: il colore deve dirlo. */
7612
- isHintError = computed(() => ['memberUnknown', 'memberNotWritable', 'globalPathInvalid'].includes(this.valueState()), ...(ngDevMode ? [{ debugName: "isHintError" }] : []));
7857
+ isHintError = computed(() => {
7858
+ const state = this.valueState();
7859
+ if (state === 'priorValuesMissing') {
7860
+ return this.priorValuesSeverity() === 'error';
7861
+ }
7862
+ return ['memberUnknown', 'memberNotWritable', 'globalPathInvalid'].includes(state);
7863
+ }, ...(ngDevMode ? [{ debugName: "isHintError" }] : []));
7613
7864
  errorMessage = computed(() => this.loadError(), ...(ngDevMode ? [{ debugName: "errorMessage" }] : []));
7614
7865
  open() {
7615
7866
  if (this.disabled()) {
@@ -8849,10 +9100,17 @@ class FormulaEditorComponent {
8849
9100
  // Con `commitOn="change"` il `change` del DOM non scatta su una scrittura da codice.
8850
9101
  this.onCommit(next);
8851
9102
  }
8852
- async loadFunctions(usage) {
9103
+ /**
9104
+ * §6.3 — quando ci sono i **valori precedenti** del record: da qui dipende se la palette propone
9105
+ * `PRIORVALUE` e simili. E' un `computed` e non una lettura dentro l'effetto perche' il documento
9106
+ * cambia a ogni battuta mentre questa risposta cambia quasi mai: cosi' la palette si ricarica
9107
+ * quando cambia l'**operazione**, non quando si scrive.
9108
+ */
9109
+ priorValues = computed(() => priorValuesOfDefinition(this.store.document()), ...(ngDevMode ? [{ debugName: "priorValues" }] : []));
9110
+ async loadFunctions(usage, availability) {
8853
9111
  // La primitiva e' opzionale: in errore lo store risponde lista vuota, cioe' «non dichiarate».
8854
- const list = await this.catalog.listFormulaFunctions(usage);
8855
- if (this.usage() === usage) {
9112
+ const list = await this.catalog.listFormulaFunctions(usage, availability);
9113
+ if (this.usage() === usage && this.priorValues() === availability) {
8856
9114
  this.functions.set(list);
8857
9115
  }
8858
9116
  }
@@ -8895,7 +9153,8 @@ class FormulaEditorComponent {
8895
9153
  // montati insieme non fanno piu' di una chiamata per uso.
8896
9154
  effect(() => {
8897
9155
  const usage = this.usage();
8898
- untracked(() => void this.loadFunctions(usage));
9156
+ const availability = this.priorValues();
9157
+ untracked(() => void this.loadFunctions(usage, availability));
8899
9158
  });
8900
9159
  effect(() => {
8901
9160
  const expression = this.expression();
@@ -9501,6 +9760,13 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.31", ngImpo
9501
9760
  *
9502
9761
  * Inoltre: `WasVisited` e `HasError` vogliono a sinistra il nome di un **node**, non di una
9503
9762
  * risorsa (`appliesToElements`), e il selettore cambia di conseguenza.
9763
+ *
9764
+ * E **i valori precedenti del record non ci sono sempre** (§7). Una condizione che li legge —
9765
+ * `IsChanged`, o un riferimento radicato su `$Record__Prior` — vale quanto l'operazione che innesca
9766
+ * il flow: sull'aggiornamento non c'e' niente da dire, su «creazione e aggiornamento» il flow
9767
+ * funziona a meta' (avviso), sulla creazione e sull'eliminazione a runtime l'interview **fallisce**
9768
+ * davanti a chi stava salvando (errore). La regola sta in `core/record-trigger.ts`, ed e' la stessa
9769
+ * che applica il validatore: qui si dice mentre si scrive la condizione, che e' mezz'ora prima.
9504
9770
  */
9505
9771
  class ConditionEditorComponent {
9506
9772
  dictionaries = inject(FlowDictionaryStore);
@@ -9620,6 +9886,37 @@ class ConditionEditorComponent {
9620
9886
  }
9621
9887
  return !areTypesComparable(this.leftDataType(condition), this.rightDataType(condition));
9622
9888
  }
9889
+ /**
9890
+ * §3.4, §7 — quando ci sono i valori precedenti del record in **questo** documento: dipende
9891
+ * dall'operazione che innesca il flow, che sta nello Start.
9892
+ */
9893
+ priorValues = computed(() => priorValuesOfDefinition(this.store.document()), ...(ngDevMode ? [{ debugName: "priorValues" }] : []));
9894
+ /**
9895
+ * La condizione legge i valori precedenti? Tre modi di farlo, e vanno guardati tutti e tre:
9896
+ * l'operatore `IsChanged`, il riferimento di sinistra e quello di confronto. Le **formule**
9897
+ * restano fuori di proposito: la sintassi la conosce solo il motore (§6.3).
9898
+ */
9899
+ readsPriorValues(condition) {
9900
+ return (condition.operator === PRIOR_VALUES_OPERATOR ||
9901
+ readsPriorValues(condition.leftValueReference) ||
9902
+ readsPriorValues(condition.rightValue?.elementReference));
9903
+ }
9904
+ /** `none` dove non c'e' niente da dire: e' il caso dell'aggiornamento, e di chi non li legge. */
9905
+ priorValuesSeverityOf(condition) {
9906
+ return this.readsPriorValues(condition) ? priorValuesSeverity(this.priorValues()) : 'none';
9907
+ }
9908
+ priorValuesMessage(condition) {
9909
+ const availability = this.priorValues();
9910
+ const consequence = availability === 'never'
9911
+ ? ': a runtime l’interview fallisce'
9912
+ : availability === 'sometimes'
9913
+ ? ': il flow funziona a meta’'
9914
+ : '';
9915
+ const what = condition.operator === PRIOR_VALUES_OPERATOR
9916
+ ? `L’operatore «${this.operatorLabel(condition.operator)}» legge i valori precedenti del record`
9917
+ : 'Questa condizione legge i valori precedenti del record';
9918
+ return `${what}, ma ${describePriorValues(availability)}${consequence} (PRIOR_VALUES_NOT_AVAILABLE).`;
9919
+ }
9623
9920
  typeMismatchMessage(condition) {
9624
9921
  const left = this.leftDataType(condition) ?? '?';
9625
9922
  const right = this.rightDataType(condition) ?? '?';
@@ -9803,11 +10100,11 @@ class ConditionEditorComponent {
9803
10100
  });
9804
10101
  }
9805
10102
  static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.3.31", ngImport: i0, type: ConditionEditorComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
9806
- static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "20.3.31", type: ConditionEditorComponent, isStandalone: true, selector: "fb-condition-editor", inputs: { holder: { classPropertyName: "holder", publicName: "holder", isSignal: true, isRequired: true, transformFunction: null }, title: { classPropertyName: "title", publicName: "title", isSignal: true, isRequired: false, transformFunction: null }, allowFormula: { classPropertyName: "allowFormula", publicName: "allowFormula", isSignal: true, isRequired: false, transformFunction: null }, preferredRoots: { classPropertyName: "preferredRoots", publicName: "preferredRoots", isSignal: true, isRequired: false, transformFunction: null }, allowLogic: { classPropertyName: "allowLogic", publicName: "allowLogic", isSignal: true, isRequired: false, transformFunction: null }, issuePath: { classPropertyName: "issuePath", publicName: "issuePath", isSignal: true, isRequired: false, transformFunction: null } }, outputs: { changed: "changed" }, ngImport: i0, template: "<fieldset class=\"fb-section\">\r\n <legend class=\"fb-section__title\">{{ title() }}</legend>\r\n\r\n @if (allowLogic()) {\r\n <div class=\"fb-cond__logic\">\r\n <label class=\"fb-field__label\">Come si combinano</label>\r\n <div class=\"fb-cond__modes\" role=\"group\" aria-label=\"Logica delle condizioni\">\r\n <button\r\n type=\"button\"\r\n class=\"fb-cond__mode\"\r\n [class.fb-cond__mode--active]=\"logicMode() === 'and'\"\r\n title=\"Tutte le condizioni devono essere vere\"\r\n (click)=\"setLogicMode('and')\"\r\n >\r\n Tutte (AND)\r\n </button>\r\n <button\r\n type=\"button\"\r\n class=\"fb-cond__mode\"\r\n [class.fb-cond__mode--active]=\"logicMode() === 'or'\"\r\n title=\"Almeno una condizione deve essere vera\"\r\n (click)=\"setLogicMode('or')\"\r\n >\r\n Almeno una (OR)\r\n </button>\r\n <button\r\n type=\"button\"\r\n class=\"fb-cond__mode\"\r\n [class.fb-cond__mode--active]=\"logicMode() === 'custom'\"\r\n title=\"Espressione sugli indici delle condizioni, es. 1 AND (2 OR 3)\"\r\n (click)=\"setLogicMode('custom')\"\r\n >\r\n Espressione\r\n </button>\r\n @if (allowFormula()) {\r\n <button\r\n type=\"button\"\r\n class=\"fb-cond__mode\"\r\n [class.fb-cond__mode--active]=\"logicMode() === 'formula'\"\r\n title=\"L\u2019esito lo determina una formula: le condizioni vengono ignorate\"\r\n (click)=\"setLogicMode('formula')\"\r\n >\r\n Formula\r\n </button>\r\n }\r\n </div>\r\n </div>\r\n } @else {\r\n <p class=\"fb-field__hint\">\r\n Devono essere vere <strong>tutte</strong>: qui il modello non prevede una logica separata.\r\n </p>\r\n }\r\n\r\n @if (logicMode() === 'custom') {\r\n <div class=\"fb-field\">\r\n <input\r\n class=\"fb-input fb-input--mono\"\r\n [class.fb-input--invalid]=\"!!customLogicError()\"\r\n [value]=\"customLogic()\"\r\n placeholder=\"1 AND (2 OR 3)\"\r\n aria-label=\"Espressione sugli indici delle condizioni\"\r\n (input)=\"setCustomLogic($any($event.target).value)\"\r\n />\r\n @if (customLogicError()) {\r\n <p class=\"fb-field__error\">{{ customLogicError() }}</p>\r\n } @else {\r\n <p class=\"fb-field__hint\">\r\n Gli indici sono 1-based e si riferiscono all\u2019ordine sotto. Cancellare una condizione riscrive\r\n l\u2019espressione automaticamente.\r\n </p>\r\n }\r\n </div>\r\n }\r\n\r\n @if (logicMode() === 'formula') {\r\n <div class=\"fb-field\">\r\n <label class=\"fb-field__label fb-field__label--required\">Formula</label>\r\n <!-- Una condizione deve produrre un booleano: mandarlo e' cio' che fa dire al motore\r\n \u00ABqui serve un booleano\u00BB invece di accettare un'espressione che fallira' (\u00A76.3). -->\r\n <fb-formula-editor\r\n [expression]=\"holder().formula || ''\"\r\n usage=\"Condition\"\r\n expectedDataType=\"Boolean\"\r\n placeholder=\"AND(Esito = 'KO', Importo > 1000)\"\r\n ariaLabel=\"Formula della condizione\"\r\n (expressionChange)=\"setFormula($event)\"\r\n >\r\n <p class=\"fb-field__hint\">\r\n Con la modalita\u2019 Formula le condizioni sotto vengono ignorate dal motore.\r\n </p>\r\n </fb-formula-editor>\r\n </div>\r\n }\r\n\r\n @if (conditionsIgnored() && conditions().length) {\r\n <p class=\"fb-callout fb-callout--warn\">\r\n Ci sono {{ conditions().length }} condizioni ma la logica e\u2019 \u00ABFormula\u00BB: il motore le ignora.\r\n </p>\r\n }\r\n\r\n @if (incompleteCount()) {\r\n <!-- `None` blocca l'attivazione: la bozza si salva, la versione attiva no (\u00A713.14). -->\r\n <p class=\"fb-callout fb-callout--error\">\r\n {{ incompleteCount() === 1 ? 'Una condizione e\u2019' : incompleteCount() + ' condizioni sono' }} da\r\n completare: la bozza si salva, l\u2019attivazione no (CONDITION_INCOMPLETE).\r\n </p>\r\n }\r\n\r\n <div class=\"fb-list\">\r\n @for (condition of conditions(); track $index) {\r\n <div class=\"fb-list__item\">\r\n <div class=\"fb-list__header\">\r\n <!-- L'indice e' 1-based perche' e' quello che l'espressione referenzia. -->\r\n <span class=\"fb-list__index\" [title]=\"'Indice ' + ($index + 1) + ' nell\u2019espressione'\">\r\n {{ $index + 1 }}\r\n </span>\r\n @if (isPlaceholderOperator(condition)) {\r\n <span\r\n class=\"fb-cond__placeholder\"\r\n title=\"A runtime vale sempre falso, e l\u2019attivazione la rifiuta: va completata\"\r\n >\r\n da completare\r\n </span>\r\n }\r\n <span class=\"fb-list__spacer\"></span>\r\n <button\r\n type=\"button\"\r\n class=\"fb-btn fb-btn--ghost fb-btn--icon\"\r\n aria-label=\"Sposta su\"\r\n [disabled]=\"$first\"\r\n (click)=\"moveCondition($index, -1)\"\r\n >\r\n \u2191\r\n </button>\r\n <button\r\n type=\"button\"\r\n class=\"fb-btn fb-btn--ghost fb-btn--icon\"\r\n aria-label=\"Sposta giu\u2019\"\r\n [disabled]=\"$last\"\r\n (click)=\"moveCondition($index, 1)\"\r\n >\r\n \u2193\r\n </button>\r\n <button\r\n type=\"button\"\r\n class=\"fb-btn fb-btn--ghost fb-btn--icon\"\r\n aria-label=\"Rimuovi la condizione\"\r\n (click)=\"removeCondition($index)\"\r\n >\r\n \u00D7\r\n </button>\r\n </div>\r\n\r\n <!-- I tre termini del confronto sono una frase sola: si leggono in riga. -->\r\n <div class=\"fb-fields-row\">\r\n <div class=\"fb-field\">\r\n <label class=\"fb-field__label\">\r\n {{ appliesToElements(condition) ? 'Elemento' : 'Risorsa' }}\r\n </label>\r\n <fb-reference-picker\r\n [value]=\"condition.leftValueReference\"\r\n [elementsOnly]=\"appliesToElements(condition)\"\r\n [preferredRoots]=\"preferredRoots()\"\r\n [placeholder]=\"appliesToElements(condition) ? 'Scegli un elemento del flow' : 'Scegli una risorsa'\"\r\n (valueChange)=\"setLeft($index, $event)\"\r\n />\r\n @if (appliesToElements(condition)) {\r\n <p class=\"fb-field__hint\">\r\n Questo operatore si applica a un elemento del flow, non a una risorsa.\r\n </p>\r\n }\r\n </div>\r\n\r\n <div class=\"fb-field fb-field--compact\">\r\n <label class=\"fb-field__label\">Operatore</label>\r\n <select\r\n class=\"fb-select\"\r\n [class.fb-input--invalid]=\"isPlaceholderOperator(condition)\"\r\n [fbValue]=\"condition.operator || ''\"\r\n (change)=\"setOperator($index, $any($event.target).value)\"\r\n >\r\n <!-- Filtrati per il tipo del lato sinistro: `appliesTo` del dizionario (\u00A74.3). -->\r\n @for (operator of operatorsFor(condition); track operator.value) {\r\n <option [value]=\"operator.value\">{{ operator.label }}</option>\r\n }\r\n </select>\r\n @if (isPlaceholderOperator(condition)) {\r\n <p class=\"fb-field__error\">\r\n Segnaposto: a runtime vale sempre falso e l\u2019attivazione lo rifiuta\r\n (CONDITION_INCOMPLETE). Scegli un operatore.\r\n </p>\r\n } @else if (operatorDescription(condition.operator)) {\r\n <p class=\"fb-field__hint\">{{ operatorDescription(condition.operator) }}</p>\r\n }\r\n </div>\r\n\r\n @if (isUnary(condition)) {\r\n <!--\r\n Trappola numero uno: per gli operatori unari `rightValue` non e' il termine di\r\n confronto ma l'esito atteso. Qui non c'e' un campo \"valore da confrontare\":\r\n c'e' un selettore che dice quale delle due cose si sta chiedendo.\r\n -->\r\n <div class=\"fb-field\">\r\n <label class=\"fb-field__label\">Esito atteso</label>\r\n <div class=\"fb-cond__unary\" role=\"group\" aria-label=\"Esito atteso\">\r\n <button\r\n type=\"button\"\r\n class=\"fb-cond__mode\"\r\n [class.fb-cond__mode--active]=\"unaryExpectation(condition)\"\r\n (click)=\"setUnaryExpectation($index, true)\"\r\n >\r\n {{ operatorLabel(condition.operator) }}\r\n </button>\r\n <button\r\n type=\"button\"\r\n class=\"fb-cond__mode\"\r\n [class.fb-cond__mode--active]=\"!unaryExpectation(condition)\"\r\n (click)=\"setUnaryExpectation($index, false)\"\r\n >\r\n NON {{ operatorLabel(condition.operator) }}\r\n </button>\r\n </div>\r\n <p class=\"fb-field__hint\">\r\n Questa condizione e\u2019 vera quando: <strong>{{ unarySummary(condition) }}</strong>.\r\n </p>\r\n </div>\r\n } @else {\r\n <div class=\"fb-field\">\r\n <label class=\"fb-field__label\">Confronta con</label>\r\n <!--\r\n Il tipo del lato sinistro guida il secondo operando: il letterale finisce nel\r\n campo giusto e le risorse proposte sono quelle compatibili. Testo con numero e'\r\n CONDITION_TYPE_MISMATCH e blocca l'attivazione (\u00A74.3).\r\n -->\r\n <fb-value-editor\r\n [value]=\"condition.rightValue\"\r\n label=\"Valore di confronto\"\r\n [dataType]=\"leftDataType(condition)\"\r\n [objectType]=\"leftObjectType(condition)\"\r\n [preferredRoots]=\"preferredRoots()\"\r\n (valueChange)=\"setRightValue($index, $event)\"\r\n />\r\n @if (hasTypeMismatch(condition)) {\r\n <p class=\"fb-field__error\">{{ typeMismatchMessage(condition) }}</p>\r\n }\r\n </div>\r\n }\r\n </div>\r\n </div>\r\n } @empty {\r\n <p class=\"fb-empty\">Nessuna condizione.</p>\r\n }\r\n </div>\r\n\r\n <button type=\"button\" class=\"fb-btn\" (click)=\"addCondition()\">Aggiungi condizione</button>\r\n</fieldset>\r\n", styles: [":host{display:block}.fb-cond__logic{margin-bottom:8px}.fb-cond__modes,.fb-cond__unary{margin-top:3px}.fb-cond__placeholder{padding:1px 6px;border-radius:8px;background:color-mix(in srgb,var(--fb-warning, #b7791f) 14%,transparent);font-size:10px;font-weight:600;color:var(--fb-warning, #b7791f)}\n"], dependencies: [{ kind: "component", type: FormulaEditorComponent, selector: "fb-formula-editor", inputs: ["expression", "usage", "expectedDataType", "scale", "placeholder", "ariaLabel", "disabled", "rows", "preferredRoots", "commitOn"], outputs: ["expressionChange"] }, { kind: "component", type: ReferencePickerComponent, selector: "fb-reference-picker", inputs: ["value", "label", "placeholder", "disabled", "dataType", "isCollection", "objectType", "writableOnly", "elementsOnly", "extraReferences", "preferredRoots"], outputs: ["valueChange"] }, { kind: "component", type: ValueEditorComponent, selector: "fb-value-editor", inputs: ["value", "label", "dataType", "objectType", "isCollection", "valueSet", "disabled", "allowFormula", "preferredRoots"], outputs: ["valueChange"] }, { kind: "directive", type: SelectValueDirective, selector: "select[fbValue]", inputs: ["fbValue"] }], changeDetection: i0.ChangeDetectionStrategy.OnPush });
10103
+ static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "20.3.31", type: ConditionEditorComponent, isStandalone: true, selector: "fb-condition-editor", inputs: { holder: { classPropertyName: "holder", publicName: "holder", isSignal: true, isRequired: true, transformFunction: null }, title: { classPropertyName: "title", publicName: "title", isSignal: true, isRequired: false, transformFunction: null }, allowFormula: { classPropertyName: "allowFormula", publicName: "allowFormula", isSignal: true, isRequired: false, transformFunction: null }, preferredRoots: { classPropertyName: "preferredRoots", publicName: "preferredRoots", isSignal: true, isRequired: false, transformFunction: null }, allowLogic: { classPropertyName: "allowLogic", publicName: "allowLogic", isSignal: true, isRequired: false, transformFunction: null }, issuePath: { classPropertyName: "issuePath", publicName: "issuePath", isSignal: true, isRequired: false, transformFunction: null } }, outputs: { changed: "changed" }, ngImport: i0, template: "<fieldset class=\"fb-section\">\r\n <legend class=\"fb-section__title\">{{ title() }}</legend>\r\n\r\n @if (allowLogic()) {\r\n <div class=\"fb-cond__logic\">\r\n <label class=\"fb-field__label\">Come si combinano</label>\r\n <div class=\"fb-cond__modes\" role=\"group\" aria-label=\"Logica delle condizioni\">\r\n <button\r\n type=\"button\"\r\n class=\"fb-cond__mode\"\r\n [class.fb-cond__mode--active]=\"logicMode() === 'and'\"\r\n title=\"Tutte le condizioni devono essere vere\"\r\n (click)=\"setLogicMode('and')\"\r\n >\r\n Tutte (AND)\r\n </button>\r\n <button\r\n type=\"button\"\r\n class=\"fb-cond__mode\"\r\n [class.fb-cond__mode--active]=\"logicMode() === 'or'\"\r\n title=\"Almeno una condizione deve essere vera\"\r\n (click)=\"setLogicMode('or')\"\r\n >\r\n Almeno una (OR)\r\n </button>\r\n <button\r\n type=\"button\"\r\n class=\"fb-cond__mode\"\r\n [class.fb-cond__mode--active]=\"logicMode() === 'custom'\"\r\n title=\"Espressione sugli indici delle condizioni, es. 1 AND (2 OR 3)\"\r\n (click)=\"setLogicMode('custom')\"\r\n >\r\n Espressione\r\n </button>\r\n @if (allowFormula()) {\r\n <button\r\n type=\"button\"\r\n class=\"fb-cond__mode\"\r\n [class.fb-cond__mode--active]=\"logicMode() === 'formula'\"\r\n title=\"L\u2019esito lo determina una formula: le condizioni vengono ignorate\"\r\n (click)=\"setLogicMode('formula')\"\r\n >\r\n Formula\r\n </button>\r\n }\r\n </div>\r\n </div>\r\n } @else {\r\n <p class=\"fb-field__hint\">\r\n Devono essere vere <strong>tutte</strong>: qui il modello non prevede una logica separata.\r\n </p>\r\n }\r\n\r\n @if (logicMode() === 'custom') {\r\n <div class=\"fb-field\">\r\n <input\r\n class=\"fb-input fb-input--mono\"\r\n [class.fb-input--invalid]=\"!!customLogicError()\"\r\n [value]=\"customLogic()\"\r\n placeholder=\"1 AND (2 OR 3)\"\r\n aria-label=\"Espressione sugli indici delle condizioni\"\r\n (input)=\"setCustomLogic($any($event.target).value)\"\r\n />\r\n @if (customLogicError()) {\r\n <p class=\"fb-field__error\">{{ customLogicError() }}</p>\r\n } @else {\r\n <p class=\"fb-field__hint\">\r\n Gli indici sono 1-based e si riferiscono all\u2019ordine sotto. Cancellare una condizione riscrive\r\n l\u2019espressione automaticamente.\r\n </p>\r\n }\r\n </div>\r\n }\r\n\r\n @if (logicMode() === 'formula') {\r\n <div class=\"fb-field\">\r\n <label class=\"fb-field__label fb-field__label--required\">Formula</label>\r\n <!-- Una condizione deve produrre un booleano: mandarlo e' cio' che fa dire al motore\r\n \u00ABqui serve un booleano\u00BB invece di accettare un'espressione che fallira' (\u00A76.3). -->\r\n <fb-formula-editor\r\n [expression]=\"holder().formula || ''\"\r\n usage=\"Condition\"\r\n expectedDataType=\"Boolean\"\r\n placeholder=\"AND(Esito = 'KO', Importo > 1000)\"\r\n ariaLabel=\"Formula della condizione\"\r\n (expressionChange)=\"setFormula($event)\"\r\n >\r\n <p class=\"fb-field__hint\">\r\n Con la modalita\u2019 Formula le condizioni sotto vengono ignorate dal motore.\r\n </p>\r\n </fb-formula-editor>\r\n </div>\r\n }\r\n\r\n @if (conditionsIgnored() && conditions().length) {\r\n <p class=\"fb-callout fb-callout--warn\">\r\n Ci sono {{ conditions().length }} condizioni ma la logica e\u2019 \u00ABFormula\u00BB: il motore le ignora.\r\n </p>\r\n }\r\n\r\n @if (incompleteCount()) {\r\n <!-- `None` blocca l'attivazione: la bozza si salva, la versione attiva no (\u00A713.14). -->\r\n <p class=\"fb-callout fb-callout--error\">\r\n {{ incompleteCount() === 1 ? 'Una condizione e\u2019' : incompleteCount() + ' condizioni sono' }} da\r\n completare: la bozza si salva, l\u2019attivazione no (CONDITION_INCOMPLETE).\r\n </p>\r\n }\r\n\r\n <div class=\"fb-list\">\r\n @for (condition of conditions(); track $index) {\r\n <div class=\"fb-list__item\">\r\n <div class=\"fb-list__header\">\r\n <!-- L'indice e' 1-based perche' e' quello che l'espressione referenzia. -->\r\n <span class=\"fb-list__index\" [title]=\"'Indice ' + ($index + 1) + ' nell\u2019espressione'\">\r\n {{ $index + 1 }}\r\n </span>\r\n @if (isPlaceholderOperator(condition)) {\r\n <span\r\n class=\"fb-cond__placeholder\"\r\n title=\"A runtime vale sempre falso, e l\u2019attivazione la rifiuta: va completata\"\r\n >\r\n da completare\r\n </span>\r\n }\r\n <span class=\"fb-list__spacer\"></span>\r\n <button\r\n type=\"button\"\r\n class=\"fb-btn fb-btn--ghost fb-btn--icon\"\r\n aria-label=\"Sposta su\"\r\n [disabled]=\"$first\"\r\n (click)=\"moveCondition($index, -1)\"\r\n >\r\n \u2191\r\n </button>\r\n <button\r\n type=\"button\"\r\n class=\"fb-btn fb-btn--ghost fb-btn--icon\"\r\n aria-label=\"Sposta giu\u2019\"\r\n [disabled]=\"$last\"\r\n (click)=\"moveCondition($index, 1)\"\r\n >\r\n \u2193\r\n </button>\r\n <button\r\n type=\"button\"\r\n class=\"fb-btn fb-btn--ghost fb-btn--icon\"\r\n aria-label=\"Rimuovi la condizione\"\r\n (click)=\"removeCondition($index)\"\r\n >\r\n \u00D7\r\n </button>\r\n </div>\r\n\r\n <!-- I tre termini del confronto sono una frase sola: si leggono in riga. -->\r\n <div class=\"fb-fields-row\">\r\n <div class=\"fb-field\">\r\n <label class=\"fb-field__label\">\r\n {{ appliesToElements(condition) ? 'Elemento' : 'Risorsa' }}\r\n </label>\r\n <fb-reference-picker\r\n [value]=\"condition.leftValueReference\"\r\n [elementsOnly]=\"appliesToElements(condition)\"\r\n [preferredRoots]=\"preferredRoots()\"\r\n [placeholder]=\"appliesToElements(condition) ? 'Scegli un elemento del flow' : 'Scegli una risorsa'\"\r\n (valueChange)=\"setLeft($index, $event)\"\r\n />\r\n @if (appliesToElements(condition)) {\r\n <p class=\"fb-field__hint\">\r\n Questo operatore si applica a un elemento del flow, non a una risorsa.\r\n </p>\r\n }\r\n </div>\r\n\r\n <div class=\"fb-field fb-field--compact\">\r\n <label class=\"fb-field__label\">Operatore</label>\r\n <select\r\n class=\"fb-select\"\r\n [class.fb-input--invalid]=\"isPlaceholderOperator(condition)\"\r\n [fbValue]=\"condition.operator || ''\"\r\n (change)=\"setOperator($index, $any($event.target).value)\"\r\n >\r\n <!-- Filtrati per il tipo del lato sinistro: `appliesTo` del dizionario (\u00A74.3). -->\r\n @for (operator of operatorsFor(condition); track operator.value) {\r\n <option [value]=\"operator.value\">{{ operator.label }}</option>\r\n }\r\n </select>\r\n @if (isPlaceholderOperator(condition)) {\r\n <p class=\"fb-field__error\">\r\n Segnaposto: a runtime vale sempre falso e l\u2019attivazione lo rifiuta\r\n (CONDITION_INCOMPLETE). Scegli un operatore.\r\n </p>\r\n } @else if (operatorDescription(condition.operator)) {\r\n <p class=\"fb-field__hint\">{{ operatorDescription(condition.operator) }}</p>\r\n }\r\n <!--\r\n \u00A77 \u2014 i valori precedenti del record non ci sono sempre, e quanto costa leggerli lo\r\n decide l'**operazione** che innesca il flow: mai = l'interview fallisce (errore), a\r\n volte = funziona a meta' (avviso). Dirlo qui e' dirlo prima che lo dica il runtime.\r\n -->\r\n @if (priorValuesSeverityOf(condition) === 'error') {\r\n <p class=\"fb-field__error\">{{ priorValuesMessage(condition) }}</p>\r\n } @else if (priorValuesSeverityOf(condition) === 'warning') {\r\n <p class=\"fb-field__hint fb-field__hint--warn\">{{ priorValuesMessage(condition) }}</p>\r\n }\r\n </div>\r\n\r\n @if (isUnary(condition)) {\r\n <!--\r\n Trappola numero uno: per gli operatori unari `rightValue` non e' il termine di\r\n confronto ma l'esito atteso. Qui non c'e' un campo \"valore da confrontare\":\r\n c'e' un selettore che dice quale delle due cose si sta chiedendo.\r\n -->\r\n <div class=\"fb-field\">\r\n <label class=\"fb-field__label\">Esito atteso</label>\r\n <div class=\"fb-cond__unary\" role=\"group\" aria-label=\"Esito atteso\">\r\n <button\r\n type=\"button\"\r\n class=\"fb-cond__mode\"\r\n [class.fb-cond__mode--active]=\"unaryExpectation(condition)\"\r\n (click)=\"setUnaryExpectation($index, true)\"\r\n >\r\n {{ operatorLabel(condition.operator) }}\r\n </button>\r\n <button\r\n type=\"button\"\r\n class=\"fb-cond__mode\"\r\n [class.fb-cond__mode--active]=\"!unaryExpectation(condition)\"\r\n (click)=\"setUnaryExpectation($index, false)\"\r\n >\r\n NON {{ operatorLabel(condition.operator) }}\r\n </button>\r\n </div>\r\n <p class=\"fb-field__hint\">\r\n Questa condizione e\u2019 vera quando: <strong>{{ unarySummary(condition) }}</strong>.\r\n </p>\r\n </div>\r\n } @else {\r\n <div class=\"fb-field\">\r\n <label class=\"fb-field__label\">Confronta con</label>\r\n <!--\r\n Il tipo del lato sinistro guida il secondo operando: il letterale finisce nel\r\n campo giusto e le risorse proposte sono quelle compatibili. Testo con numero e'\r\n CONDITION_TYPE_MISMATCH e blocca l'attivazione (\u00A74.3).\r\n -->\r\n <fb-value-editor\r\n [value]=\"condition.rightValue\"\r\n label=\"Valore di confronto\"\r\n [dataType]=\"leftDataType(condition)\"\r\n [objectType]=\"leftObjectType(condition)\"\r\n [preferredRoots]=\"preferredRoots()\"\r\n (valueChange)=\"setRightValue($index, $event)\"\r\n />\r\n @if (hasTypeMismatch(condition)) {\r\n <p class=\"fb-field__error\">{{ typeMismatchMessage(condition) }}</p>\r\n }\r\n </div>\r\n }\r\n </div>\r\n </div>\r\n } @empty {\r\n <p class=\"fb-empty\">Nessuna condizione.</p>\r\n }\r\n </div>\r\n\r\n <button type=\"button\" class=\"fb-btn\" (click)=\"addCondition()\">Aggiungi condizione</button>\r\n</fieldset>\r\n", styles: [":host{display:block}.fb-cond__logic{margin-bottom:8px}.fb-cond__modes,.fb-cond__unary{margin-top:3px}.fb-cond__placeholder{padding:1px 6px;border-radius:8px;background:color-mix(in srgb,var(--fb-warning, #b7791f) 14%,transparent);font-size:10px;font-weight:600;color:var(--fb-warning, #b7791f)}\n"], dependencies: [{ kind: "component", type: FormulaEditorComponent, selector: "fb-formula-editor", inputs: ["expression", "usage", "expectedDataType", "scale", "placeholder", "ariaLabel", "disabled", "rows", "preferredRoots", "commitOn"], outputs: ["expressionChange"] }, { kind: "component", type: ReferencePickerComponent, selector: "fb-reference-picker", inputs: ["value", "label", "placeholder", "disabled", "dataType", "isCollection", "objectType", "writableOnly", "elementsOnly", "extraReferences", "preferredRoots"], outputs: ["valueChange"] }, { kind: "component", type: ValueEditorComponent, selector: "fb-value-editor", inputs: ["value", "label", "dataType", "objectType", "isCollection", "valueSet", "disabled", "allowFormula", "preferredRoots"], outputs: ["valueChange"] }, { kind: "directive", type: SelectValueDirective, selector: "select[fbValue]", inputs: ["fbValue"] }], changeDetection: i0.ChangeDetectionStrategy.OnPush });
9807
10104
  }
9808
10105
  i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.31", ngImport: i0, type: ConditionEditorComponent, decorators: [{
9809
10106
  type: Component,
9810
- args: [{ selector: 'fb-condition-editor', standalone: true, imports: [FormulaEditorComponent, ReferencePickerComponent, ValueEditorComponent, SelectValueDirective], changeDetection: ChangeDetectionStrategy.OnPush, template: "<fieldset class=\"fb-section\">\r\n <legend class=\"fb-section__title\">{{ title() }}</legend>\r\n\r\n @if (allowLogic()) {\r\n <div class=\"fb-cond__logic\">\r\n <label class=\"fb-field__label\">Come si combinano</label>\r\n <div class=\"fb-cond__modes\" role=\"group\" aria-label=\"Logica delle condizioni\">\r\n <button\r\n type=\"button\"\r\n class=\"fb-cond__mode\"\r\n [class.fb-cond__mode--active]=\"logicMode() === 'and'\"\r\n title=\"Tutte le condizioni devono essere vere\"\r\n (click)=\"setLogicMode('and')\"\r\n >\r\n Tutte (AND)\r\n </button>\r\n <button\r\n type=\"button\"\r\n class=\"fb-cond__mode\"\r\n [class.fb-cond__mode--active]=\"logicMode() === 'or'\"\r\n title=\"Almeno una condizione deve essere vera\"\r\n (click)=\"setLogicMode('or')\"\r\n >\r\n Almeno una (OR)\r\n </button>\r\n <button\r\n type=\"button\"\r\n class=\"fb-cond__mode\"\r\n [class.fb-cond__mode--active]=\"logicMode() === 'custom'\"\r\n title=\"Espressione sugli indici delle condizioni, es. 1 AND (2 OR 3)\"\r\n (click)=\"setLogicMode('custom')\"\r\n >\r\n Espressione\r\n </button>\r\n @if (allowFormula()) {\r\n <button\r\n type=\"button\"\r\n class=\"fb-cond__mode\"\r\n [class.fb-cond__mode--active]=\"logicMode() === 'formula'\"\r\n title=\"L\u2019esito lo determina una formula: le condizioni vengono ignorate\"\r\n (click)=\"setLogicMode('formula')\"\r\n >\r\n Formula\r\n </button>\r\n }\r\n </div>\r\n </div>\r\n } @else {\r\n <p class=\"fb-field__hint\">\r\n Devono essere vere <strong>tutte</strong>: qui il modello non prevede una logica separata.\r\n </p>\r\n }\r\n\r\n @if (logicMode() === 'custom') {\r\n <div class=\"fb-field\">\r\n <input\r\n class=\"fb-input fb-input--mono\"\r\n [class.fb-input--invalid]=\"!!customLogicError()\"\r\n [value]=\"customLogic()\"\r\n placeholder=\"1 AND (2 OR 3)\"\r\n aria-label=\"Espressione sugli indici delle condizioni\"\r\n (input)=\"setCustomLogic($any($event.target).value)\"\r\n />\r\n @if (customLogicError()) {\r\n <p class=\"fb-field__error\">{{ customLogicError() }}</p>\r\n } @else {\r\n <p class=\"fb-field__hint\">\r\n Gli indici sono 1-based e si riferiscono all\u2019ordine sotto. Cancellare una condizione riscrive\r\n l\u2019espressione automaticamente.\r\n </p>\r\n }\r\n </div>\r\n }\r\n\r\n @if (logicMode() === 'formula') {\r\n <div class=\"fb-field\">\r\n <label class=\"fb-field__label fb-field__label--required\">Formula</label>\r\n <!-- Una condizione deve produrre un booleano: mandarlo e' cio' che fa dire al motore\r\n \u00ABqui serve un booleano\u00BB invece di accettare un'espressione che fallira' (\u00A76.3). -->\r\n <fb-formula-editor\r\n [expression]=\"holder().formula || ''\"\r\n usage=\"Condition\"\r\n expectedDataType=\"Boolean\"\r\n placeholder=\"AND(Esito = 'KO', Importo > 1000)\"\r\n ariaLabel=\"Formula della condizione\"\r\n (expressionChange)=\"setFormula($event)\"\r\n >\r\n <p class=\"fb-field__hint\">\r\n Con la modalita\u2019 Formula le condizioni sotto vengono ignorate dal motore.\r\n </p>\r\n </fb-formula-editor>\r\n </div>\r\n }\r\n\r\n @if (conditionsIgnored() && conditions().length) {\r\n <p class=\"fb-callout fb-callout--warn\">\r\n Ci sono {{ conditions().length }} condizioni ma la logica e\u2019 \u00ABFormula\u00BB: il motore le ignora.\r\n </p>\r\n }\r\n\r\n @if (incompleteCount()) {\r\n <!-- `None` blocca l'attivazione: la bozza si salva, la versione attiva no (\u00A713.14). -->\r\n <p class=\"fb-callout fb-callout--error\">\r\n {{ incompleteCount() === 1 ? 'Una condizione e\u2019' : incompleteCount() + ' condizioni sono' }} da\r\n completare: la bozza si salva, l\u2019attivazione no (CONDITION_INCOMPLETE).\r\n </p>\r\n }\r\n\r\n <div class=\"fb-list\">\r\n @for (condition of conditions(); track $index) {\r\n <div class=\"fb-list__item\">\r\n <div class=\"fb-list__header\">\r\n <!-- L'indice e' 1-based perche' e' quello che l'espressione referenzia. -->\r\n <span class=\"fb-list__index\" [title]=\"'Indice ' + ($index + 1) + ' nell\u2019espressione'\">\r\n {{ $index + 1 }}\r\n </span>\r\n @if (isPlaceholderOperator(condition)) {\r\n <span\r\n class=\"fb-cond__placeholder\"\r\n title=\"A runtime vale sempre falso, e l\u2019attivazione la rifiuta: va completata\"\r\n >\r\n da completare\r\n </span>\r\n }\r\n <span class=\"fb-list__spacer\"></span>\r\n <button\r\n type=\"button\"\r\n class=\"fb-btn fb-btn--ghost fb-btn--icon\"\r\n aria-label=\"Sposta su\"\r\n [disabled]=\"$first\"\r\n (click)=\"moveCondition($index, -1)\"\r\n >\r\n \u2191\r\n </button>\r\n <button\r\n type=\"button\"\r\n class=\"fb-btn fb-btn--ghost fb-btn--icon\"\r\n aria-label=\"Sposta giu\u2019\"\r\n [disabled]=\"$last\"\r\n (click)=\"moveCondition($index, 1)\"\r\n >\r\n \u2193\r\n </button>\r\n <button\r\n type=\"button\"\r\n class=\"fb-btn fb-btn--ghost fb-btn--icon\"\r\n aria-label=\"Rimuovi la condizione\"\r\n (click)=\"removeCondition($index)\"\r\n >\r\n \u00D7\r\n </button>\r\n </div>\r\n\r\n <!-- I tre termini del confronto sono una frase sola: si leggono in riga. -->\r\n <div class=\"fb-fields-row\">\r\n <div class=\"fb-field\">\r\n <label class=\"fb-field__label\">\r\n {{ appliesToElements(condition) ? 'Elemento' : 'Risorsa' }}\r\n </label>\r\n <fb-reference-picker\r\n [value]=\"condition.leftValueReference\"\r\n [elementsOnly]=\"appliesToElements(condition)\"\r\n [preferredRoots]=\"preferredRoots()\"\r\n [placeholder]=\"appliesToElements(condition) ? 'Scegli un elemento del flow' : 'Scegli una risorsa'\"\r\n (valueChange)=\"setLeft($index, $event)\"\r\n />\r\n @if (appliesToElements(condition)) {\r\n <p class=\"fb-field__hint\">\r\n Questo operatore si applica a un elemento del flow, non a una risorsa.\r\n </p>\r\n }\r\n </div>\r\n\r\n <div class=\"fb-field fb-field--compact\">\r\n <label class=\"fb-field__label\">Operatore</label>\r\n <select\r\n class=\"fb-select\"\r\n [class.fb-input--invalid]=\"isPlaceholderOperator(condition)\"\r\n [fbValue]=\"condition.operator || ''\"\r\n (change)=\"setOperator($index, $any($event.target).value)\"\r\n >\r\n <!-- Filtrati per il tipo del lato sinistro: `appliesTo` del dizionario (\u00A74.3). -->\r\n @for (operator of operatorsFor(condition); track operator.value) {\r\n <option [value]=\"operator.value\">{{ operator.label }}</option>\r\n }\r\n </select>\r\n @if (isPlaceholderOperator(condition)) {\r\n <p class=\"fb-field__error\">\r\n Segnaposto: a runtime vale sempre falso e l\u2019attivazione lo rifiuta\r\n (CONDITION_INCOMPLETE). Scegli un operatore.\r\n </p>\r\n } @else if (operatorDescription(condition.operator)) {\r\n <p class=\"fb-field__hint\">{{ operatorDescription(condition.operator) }}</p>\r\n }\r\n </div>\r\n\r\n @if (isUnary(condition)) {\r\n <!--\r\n Trappola numero uno: per gli operatori unari `rightValue` non e' il termine di\r\n confronto ma l'esito atteso. Qui non c'e' un campo \"valore da confrontare\":\r\n c'e' un selettore che dice quale delle due cose si sta chiedendo.\r\n -->\r\n <div class=\"fb-field\">\r\n <label class=\"fb-field__label\">Esito atteso</label>\r\n <div class=\"fb-cond__unary\" role=\"group\" aria-label=\"Esito atteso\">\r\n <button\r\n type=\"button\"\r\n class=\"fb-cond__mode\"\r\n [class.fb-cond__mode--active]=\"unaryExpectation(condition)\"\r\n (click)=\"setUnaryExpectation($index, true)\"\r\n >\r\n {{ operatorLabel(condition.operator) }}\r\n </button>\r\n <button\r\n type=\"button\"\r\n class=\"fb-cond__mode\"\r\n [class.fb-cond__mode--active]=\"!unaryExpectation(condition)\"\r\n (click)=\"setUnaryExpectation($index, false)\"\r\n >\r\n NON {{ operatorLabel(condition.operator) }}\r\n </button>\r\n </div>\r\n <p class=\"fb-field__hint\">\r\n Questa condizione e\u2019 vera quando: <strong>{{ unarySummary(condition) }}</strong>.\r\n </p>\r\n </div>\r\n } @else {\r\n <div class=\"fb-field\">\r\n <label class=\"fb-field__label\">Confronta con</label>\r\n <!--\r\n Il tipo del lato sinistro guida il secondo operando: il letterale finisce nel\r\n campo giusto e le risorse proposte sono quelle compatibili. Testo con numero e'\r\n CONDITION_TYPE_MISMATCH e blocca l'attivazione (\u00A74.3).\r\n -->\r\n <fb-value-editor\r\n [value]=\"condition.rightValue\"\r\n label=\"Valore di confronto\"\r\n [dataType]=\"leftDataType(condition)\"\r\n [objectType]=\"leftObjectType(condition)\"\r\n [preferredRoots]=\"preferredRoots()\"\r\n (valueChange)=\"setRightValue($index, $event)\"\r\n />\r\n @if (hasTypeMismatch(condition)) {\r\n <p class=\"fb-field__error\">{{ typeMismatchMessage(condition) }}</p>\r\n }\r\n </div>\r\n }\r\n </div>\r\n </div>\r\n } @empty {\r\n <p class=\"fb-empty\">Nessuna condizione.</p>\r\n }\r\n </div>\r\n\r\n <button type=\"button\" class=\"fb-btn\" (click)=\"addCondition()\">Aggiungi condizione</button>\r\n</fieldset>\r\n", styles: [":host{display:block}.fb-cond__logic{margin-bottom:8px}.fb-cond__modes,.fb-cond__unary{margin-top:3px}.fb-cond__placeholder{padding:1px 6px;border-radius:8px;background:color-mix(in srgb,var(--fb-warning, #b7791f) 14%,transparent);font-size:10px;font-weight:600;color:var(--fb-warning, #b7791f)}\n"] }]
10107
+ args: [{ selector: 'fb-condition-editor', standalone: true, imports: [FormulaEditorComponent, ReferencePickerComponent, ValueEditorComponent, SelectValueDirective], changeDetection: ChangeDetectionStrategy.OnPush, template: "<fieldset class=\"fb-section\">\r\n <legend class=\"fb-section__title\">{{ title() }}</legend>\r\n\r\n @if (allowLogic()) {\r\n <div class=\"fb-cond__logic\">\r\n <label class=\"fb-field__label\">Come si combinano</label>\r\n <div class=\"fb-cond__modes\" role=\"group\" aria-label=\"Logica delle condizioni\">\r\n <button\r\n type=\"button\"\r\n class=\"fb-cond__mode\"\r\n [class.fb-cond__mode--active]=\"logicMode() === 'and'\"\r\n title=\"Tutte le condizioni devono essere vere\"\r\n (click)=\"setLogicMode('and')\"\r\n >\r\n Tutte (AND)\r\n </button>\r\n <button\r\n type=\"button\"\r\n class=\"fb-cond__mode\"\r\n [class.fb-cond__mode--active]=\"logicMode() === 'or'\"\r\n title=\"Almeno una condizione deve essere vera\"\r\n (click)=\"setLogicMode('or')\"\r\n >\r\n Almeno una (OR)\r\n </button>\r\n <button\r\n type=\"button\"\r\n class=\"fb-cond__mode\"\r\n [class.fb-cond__mode--active]=\"logicMode() === 'custom'\"\r\n title=\"Espressione sugli indici delle condizioni, es. 1 AND (2 OR 3)\"\r\n (click)=\"setLogicMode('custom')\"\r\n >\r\n Espressione\r\n </button>\r\n @if (allowFormula()) {\r\n <button\r\n type=\"button\"\r\n class=\"fb-cond__mode\"\r\n [class.fb-cond__mode--active]=\"logicMode() === 'formula'\"\r\n title=\"L\u2019esito lo determina una formula: le condizioni vengono ignorate\"\r\n (click)=\"setLogicMode('formula')\"\r\n >\r\n Formula\r\n </button>\r\n }\r\n </div>\r\n </div>\r\n } @else {\r\n <p class=\"fb-field__hint\">\r\n Devono essere vere <strong>tutte</strong>: qui il modello non prevede una logica separata.\r\n </p>\r\n }\r\n\r\n @if (logicMode() === 'custom') {\r\n <div class=\"fb-field\">\r\n <input\r\n class=\"fb-input fb-input--mono\"\r\n [class.fb-input--invalid]=\"!!customLogicError()\"\r\n [value]=\"customLogic()\"\r\n placeholder=\"1 AND (2 OR 3)\"\r\n aria-label=\"Espressione sugli indici delle condizioni\"\r\n (input)=\"setCustomLogic($any($event.target).value)\"\r\n />\r\n @if (customLogicError()) {\r\n <p class=\"fb-field__error\">{{ customLogicError() }}</p>\r\n } @else {\r\n <p class=\"fb-field__hint\">\r\n Gli indici sono 1-based e si riferiscono all\u2019ordine sotto. Cancellare una condizione riscrive\r\n l\u2019espressione automaticamente.\r\n </p>\r\n }\r\n </div>\r\n }\r\n\r\n @if (logicMode() === 'formula') {\r\n <div class=\"fb-field\">\r\n <label class=\"fb-field__label fb-field__label--required\">Formula</label>\r\n <!-- Una condizione deve produrre un booleano: mandarlo e' cio' che fa dire al motore\r\n \u00ABqui serve un booleano\u00BB invece di accettare un'espressione che fallira' (\u00A76.3). -->\r\n <fb-formula-editor\r\n [expression]=\"holder().formula || ''\"\r\n usage=\"Condition\"\r\n expectedDataType=\"Boolean\"\r\n placeholder=\"AND(Esito = 'KO', Importo > 1000)\"\r\n ariaLabel=\"Formula della condizione\"\r\n (expressionChange)=\"setFormula($event)\"\r\n >\r\n <p class=\"fb-field__hint\">\r\n Con la modalita\u2019 Formula le condizioni sotto vengono ignorate dal motore.\r\n </p>\r\n </fb-formula-editor>\r\n </div>\r\n }\r\n\r\n @if (conditionsIgnored() && conditions().length) {\r\n <p class=\"fb-callout fb-callout--warn\">\r\n Ci sono {{ conditions().length }} condizioni ma la logica e\u2019 \u00ABFormula\u00BB: il motore le ignora.\r\n </p>\r\n }\r\n\r\n @if (incompleteCount()) {\r\n <!-- `None` blocca l'attivazione: la bozza si salva, la versione attiva no (\u00A713.14). -->\r\n <p class=\"fb-callout fb-callout--error\">\r\n {{ incompleteCount() === 1 ? 'Una condizione e\u2019' : incompleteCount() + ' condizioni sono' }} da\r\n completare: la bozza si salva, l\u2019attivazione no (CONDITION_INCOMPLETE).\r\n </p>\r\n }\r\n\r\n <div class=\"fb-list\">\r\n @for (condition of conditions(); track $index) {\r\n <div class=\"fb-list__item\">\r\n <div class=\"fb-list__header\">\r\n <!-- L'indice e' 1-based perche' e' quello che l'espressione referenzia. -->\r\n <span class=\"fb-list__index\" [title]=\"'Indice ' + ($index + 1) + ' nell\u2019espressione'\">\r\n {{ $index + 1 }}\r\n </span>\r\n @if (isPlaceholderOperator(condition)) {\r\n <span\r\n class=\"fb-cond__placeholder\"\r\n title=\"A runtime vale sempre falso, e l\u2019attivazione la rifiuta: va completata\"\r\n >\r\n da completare\r\n </span>\r\n }\r\n <span class=\"fb-list__spacer\"></span>\r\n <button\r\n type=\"button\"\r\n class=\"fb-btn fb-btn--ghost fb-btn--icon\"\r\n aria-label=\"Sposta su\"\r\n [disabled]=\"$first\"\r\n (click)=\"moveCondition($index, -1)\"\r\n >\r\n \u2191\r\n </button>\r\n <button\r\n type=\"button\"\r\n class=\"fb-btn fb-btn--ghost fb-btn--icon\"\r\n aria-label=\"Sposta giu\u2019\"\r\n [disabled]=\"$last\"\r\n (click)=\"moveCondition($index, 1)\"\r\n >\r\n \u2193\r\n </button>\r\n <button\r\n type=\"button\"\r\n class=\"fb-btn fb-btn--ghost fb-btn--icon\"\r\n aria-label=\"Rimuovi la condizione\"\r\n (click)=\"removeCondition($index)\"\r\n >\r\n \u00D7\r\n </button>\r\n </div>\r\n\r\n <!-- I tre termini del confronto sono una frase sola: si leggono in riga. -->\r\n <div class=\"fb-fields-row\">\r\n <div class=\"fb-field\">\r\n <label class=\"fb-field__label\">\r\n {{ appliesToElements(condition) ? 'Elemento' : 'Risorsa' }}\r\n </label>\r\n <fb-reference-picker\r\n [value]=\"condition.leftValueReference\"\r\n [elementsOnly]=\"appliesToElements(condition)\"\r\n [preferredRoots]=\"preferredRoots()\"\r\n [placeholder]=\"appliesToElements(condition) ? 'Scegli un elemento del flow' : 'Scegli una risorsa'\"\r\n (valueChange)=\"setLeft($index, $event)\"\r\n />\r\n @if (appliesToElements(condition)) {\r\n <p class=\"fb-field__hint\">\r\n Questo operatore si applica a un elemento del flow, non a una risorsa.\r\n </p>\r\n }\r\n </div>\r\n\r\n <div class=\"fb-field fb-field--compact\">\r\n <label class=\"fb-field__label\">Operatore</label>\r\n <select\r\n class=\"fb-select\"\r\n [class.fb-input--invalid]=\"isPlaceholderOperator(condition)\"\r\n [fbValue]=\"condition.operator || ''\"\r\n (change)=\"setOperator($index, $any($event.target).value)\"\r\n >\r\n <!-- Filtrati per il tipo del lato sinistro: `appliesTo` del dizionario (\u00A74.3). -->\r\n @for (operator of operatorsFor(condition); track operator.value) {\r\n <option [value]=\"operator.value\">{{ operator.label }}</option>\r\n }\r\n </select>\r\n @if (isPlaceholderOperator(condition)) {\r\n <p class=\"fb-field__error\">\r\n Segnaposto: a runtime vale sempre falso e l\u2019attivazione lo rifiuta\r\n (CONDITION_INCOMPLETE). Scegli un operatore.\r\n </p>\r\n } @else if (operatorDescription(condition.operator)) {\r\n <p class=\"fb-field__hint\">{{ operatorDescription(condition.operator) }}</p>\r\n }\r\n <!--\r\n \u00A77 \u2014 i valori precedenti del record non ci sono sempre, e quanto costa leggerli lo\r\n decide l'**operazione** che innesca il flow: mai = l'interview fallisce (errore), a\r\n volte = funziona a meta' (avviso). Dirlo qui e' dirlo prima che lo dica il runtime.\r\n -->\r\n @if (priorValuesSeverityOf(condition) === 'error') {\r\n <p class=\"fb-field__error\">{{ priorValuesMessage(condition) }}</p>\r\n } @else if (priorValuesSeverityOf(condition) === 'warning') {\r\n <p class=\"fb-field__hint fb-field__hint--warn\">{{ priorValuesMessage(condition) }}</p>\r\n }\r\n </div>\r\n\r\n @if (isUnary(condition)) {\r\n <!--\r\n Trappola numero uno: per gli operatori unari `rightValue` non e' il termine di\r\n confronto ma l'esito atteso. Qui non c'e' un campo \"valore da confrontare\":\r\n c'e' un selettore che dice quale delle due cose si sta chiedendo.\r\n -->\r\n <div class=\"fb-field\">\r\n <label class=\"fb-field__label\">Esito atteso</label>\r\n <div class=\"fb-cond__unary\" role=\"group\" aria-label=\"Esito atteso\">\r\n <button\r\n type=\"button\"\r\n class=\"fb-cond__mode\"\r\n [class.fb-cond__mode--active]=\"unaryExpectation(condition)\"\r\n (click)=\"setUnaryExpectation($index, true)\"\r\n >\r\n {{ operatorLabel(condition.operator) }}\r\n </button>\r\n <button\r\n type=\"button\"\r\n class=\"fb-cond__mode\"\r\n [class.fb-cond__mode--active]=\"!unaryExpectation(condition)\"\r\n (click)=\"setUnaryExpectation($index, false)\"\r\n >\r\n NON {{ operatorLabel(condition.operator) }}\r\n </button>\r\n </div>\r\n <p class=\"fb-field__hint\">\r\n Questa condizione e\u2019 vera quando: <strong>{{ unarySummary(condition) }}</strong>.\r\n </p>\r\n </div>\r\n } @else {\r\n <div class=\"fb-field\">\r\n <label class=\"fb-field__label\">Confronta con</label>\r\n <!--\r\n Il tipo del lato sinistro guida il secondo operando: il letterale finisce nel\r\n campo giusto e le risorse proposte sono quelle compatibili. Testo con numero e'\r\n CONDITION_TYPE_MISMATCH e blocca l'attivazione (\u00A74.3).\r\n -->\r\n <fb-value-editor\r\n [value]=\"condition.rightValue\"\r\n label=\"Valore di confronto\"\r\n [dataType]=\"leftDataType(condition)\"\r\n [objectType]=\"leftObjectType(condition)\"\r\n [preferredRoots]=\"preferredRoots()\"\r\n (valueChange)=\"setRightValue($index, $event)\"\r\n />\r\n @if (hasTypeMismatch(condition)) {\r\n <p class=\"fb-field__error\">{{ typeMismatchMessage(condition) }}</p>\r\n }\r\n </div>\r\n }\r\n </div>\r\n </div>\r\n } @empty {\r\n <p class=\"fb-empty\">Nessuna condizione.</p>\r\n }\r\n </div>\r\n\r\n <button type=\"button\" class=\"fb-btn\" (click)=\"addCondition()\">Aggiungi condizione</button>\r\n</fieldset>\r\n", styles: [":host{display:block}.fb-cond__logic{margin-bottom:8px}.fb-cond__modes,.fb-cond__unary{margin-top:3px}.fb-cond__placeholder{padding:1px 6px;border-radius:8px;background:color-mix(in srgb,var(--fb-warning, #b7791f) 14%,transparent);font-size:10px;font-weight:600;color:var(--fb-warning, #b7791f)}\n"] }]
9811
10108
  }], ctorParameters: () => [], propDecorators: { holder: [{ type: i0.Input, args: [{ isSignal: true, alias: "holder", required: true }] }], title: [{ type: i0.Input, args: [{ isSignal: true, alias: "title", required: false }] }], allowFormula: [{ type: i0.Input, args: [{ isSignal: true, alias: "allowFormula", required: false }] }], preferredRoots: [{ type: i0.Input, args: [{ isSignal: true, alias: "preferredRoots", required: false }] }], allowLogic: [{ type: i0.Input, args: [{ isSignal: true, alias: "allowLogic", required: false }] }], issuePath: [{ type: i0.Input, args: [{ isSignal: true, alias: "issuePath", required: false }] }], changed: [{ type: i0.Output, args: ["changed"] }] } });
9812
10109
 
9813
10110
  /**
@@ -9850,6 +10147,13 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.31", ngImpo
9850
10147
  class RecordFilterEditorComponent {
9851
10148
  catalog = inject(FlowCatalogStore);
9852
10149
  dictionaries = inject(FlowDictionaryStore);
10150
+ /**
10151
+ * §7 — serve per una domanda sola: i **valori precedenti** del record ci sono? Dipende
10152
+ * dall'operazione che innesca il flow, che sta nello Start, e vale per i criteri di ingresso come
10153
+ * per una condizione — un filtro `IsChanged`, o un valore che cita `$Record__Prior`, su un flow
10154
+ * che parte alla creazione non ha niente da confrontare.
10155
+ */
10156
+ documents = inject(FlowDocumentStore);
9853
10157
  holder = input.required(...(ngDevMode ? [{ debugName: "holder" }] : []));
9854
10158
  object = input(undefined, ...(ngDevMode ? [{ debugName: "object" }] : []));
9855
10159
  title = input('Filtri', ...(ngDevMode ? [{ debugName: "title" }] : []));
@@ -9970,6 +10274,24 @@ class RecordFilterEditorComponent {
9970
10274
  return checkConditionLogic(logic, this.filters().length, 'filtro');
9971
10275
  }, ...(ngDevMode ? [{ debugName: "customLogicError" }] : []));
9972
10276
  showEmptyWarning = computed(() => !!this.emptyWarning() && this.filters().length === 0, ...(ngDevMode ? [{ debugName: "showEmptyWarning" }] : []));
10277
+ /**
10278
+ * §7 — la gravita' di leggere i valori precedenti in questo filtro: `none` dove non li legge o
10279
+ * dove ci sono sempre. La regola e' quella di `core/record-trigger.ts`, la stessa del condition
10280
+ * editor e del validatore: scriverla due volte significa scriverla due volte diversa.
10281
+ */
10282
+ priorValuesSeverityOf(filter) {
10283
+ const reads = filter.operator === PRIOR_VALUES_OPERATOR || readsPriorValues(filter.value?.elementReference);
10284
+ return reads ? priorValuesSeverity(priorValuesOfDefinition(this.documents.document())) : 'none';
10285
+ }
10286
+ priorValuesMessage() {
10287
+ const availability = priorValuesOfDefinition(this.documents.document());
10288
+ const consequence = availability === 'never'
10289
+ ? ': a runtime l’interview fallisce'
10290
+ : availability === 'sometimes'
10291
+ ? ': il flow funziona a meta’'
10292
+ : '';
10293
+ return `Questo filtro legge i valori precedenti del record, ma ${describePriorValues(availability)}${consequence} (PRIOR_VALUES_NOT_AVAILABLE).`;
10294
+ }
9973
10295
  addFilter() {
9974
10296
  this.changed.emit((holder) => {
9975
10297
  holder.filters ??= [];
@@ -10115,11 +10437,11 @@ class RecordFilterEditorComponent {
10115
10437
  return this.types.valueSetOf(filter.field);
10116
10438
  }
10117
10439
  static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.3.31", ngImport: i0, type: RecordFilterEditorComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
10118
- static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "20.3.31", type: RecordFilterEditorComponent, isStandalone: true, selector: "fb-record-filter-editor", inputs: { holder: { classPropertyName: "holder", publicName: "holder", isSignal: true, isRequired: true, transformFunction: null }, object: { classPropertyName: "object", publicName: "object", isSignal: true, isRequired: false, transformFunction: null }, title: { classPropertyName: "title", publicName: "title", isSignal: true, isRequired: false, transformFunction: null }, usage: { classPropertyName: "usage", publicName: "usage", isSignal: true, isRequired: false, transformFunction: null }, fieldOptions: { classPropertyName: "fieldOptions", publicName: "fieldOptions", isSignal: true, isRequired: false, transformFunction: null }, supportsLogic: { classPropertyName: "supportsLogic", publicName: "supportsLogic", isSignal: true, isRequired: false, transformFunction: null }, supportsFormula: { classPropertyName: "supportsFormula", publicName: "supportsFormula", isSignal: true, isRequired: false, transformFunction: null }, emptyWarning: { classPropertyName: "emptyWarning", publicName: "emptyWarning", isSignal: true, isRequired: false, transformFunction: null }, emptyWarningSeverity: { classPropertyName: "emptyWarningSeverity", publicName: "emptyWarningSeverity", isSignal: true, isRequired: false, transformFunction: null } }, outputs: { changed: "changed" }, ngImport: i0, template: "<fieldset class=\"fb-section\">\r\n <legend class=\"fb-section__title\">{{ title() }}</legend>\r\n\r\n @if (!object() && !fieldOptions().length) {\r\n <p class=\"fb-field__hint\">Scegli prima un oggetto per poter filtrare sui suoi campi.</p>\r\n }\r\n\r\n @if (supportsLogic()) {\r\n <div class=\"fb-filter__modes\" role=\"group\" aria-label=\"Logica dei filtri\">\r\n <button\r\n type=\"button\"\r\n class=\"fb-filter__mode\"\r\n [class.fb-filter__mode--active]=\"logicMode() === 'and'\"\r\n (click)=\"setLogicMode('and')\"\r\n >\r\n Tutti (AND)\r\n </button>\r\n <button\r\n type=\"button\"\r\n class=\"fb-filter__mode\"\r\n [class.fb-filter__mode--active]=\"logicMode() === 'or'\"\r\n (click)=\"setLogicMode('or')\"\r\n >\r\n Almeno uno (OR)\r\n </button>\r\n <button\r\n type=\"button\"\r\n class=\"fb-filter__mode\"\r\n [class.fb-filter__mode--active]=\"logicMode() === 'custom'\"\r\n (click)=\"setLogicMode('custom')\"\r\n >\r\n Espressione\r\n </button>\r\n @if (supportsFormula()) {\r\n <button\r\n type=\"button\"\r\n class=\"fb-filter__mode\"\r\n [class.fb-filter__mode--active]=\"logicMode() === 'formula'\"\r\n (click)=\"setLogicMode('formula')\"\r\n >\r\n Formula\r\n </button>\r\n }\r\n </div>\r\n } @else {\r\n <!-- Qui il modello non ha `filterLogic`: mostrarlo lo farebbe perdere al salvataggio. -->\r\n <p class=\"fb-field__hint\">Su questo elemento i filtri sono sempre combinati in AND.</p>\r\n }\r\n\r\n @if (supportsLogic() && logicMode() === 'custom') {\r\n <div class=\"fb-field\">\r\n <input\r\n class=\"fb-input fb-input--mono\"\r\n [class.fb-input--invalid]=\"!!customLogicError()\"\r\n [value]=\"customLogic()\"\r\n placeholder=\"1 AND (2 OR 3)\"\r\n aria-label=\"Espressione sugli indici dei filtri\"\r\n (input)=\"setCustomLogic($any($event.target).value)\"\r\n />\r\n <!--\r\n Gli indici sono quelli numerati nell\u2019elenco qui sotto, e sono 1-based. Un indice\r\n inesistente e\u2019 `CONDITION_LOGIC_INVALID`, che e\u2019 un **errore**: blocca l\u2019attivazione,\r\n quindi conviene dirlo mentre si scrive e non alla validazione.\r\n -->\r\n @if (customLogicError()) {\r\n <p class=\"fb-field__error\">{{ customLogicError() }}</p>\r\n } @else {\r\n <p class=\"fb-field__hint\">\r\n Gli indici sono 1-based e si riferiscono ai filtri numerati sotto. Cancellare un filtro\r\n riscrive l\u2019espressione automaticamente.\r\n </p>\r\n }\r\n </div>\r\n }\r\n\r\n <!--\r\n `filterLogic: \"Formula\"` non e\u2019 supportato da nessun elemento (\u00A74.4): non lo scrive nessun\r\n gesto di questo editor, ma un flow scritto altrove puo\u2019 portarcelo. Si dice invece di\r\n correggerlo di soppiatto \u2014 e\u2019 l\u2019unico indizio di cosa quel flow voleva fare.\r\n -->\r\n @if (unsupportedFormulaLogic()) {\r\n <p class=\"fb-callout fb-callout--error\">\r\n <code>filterLogic: \"Formula\"</code> non e\u2019 un valore supportato: sui choice set e\u2019 un errore\r\n di validazione, altrove fa fallire l\u2019esecuzione. I filtri qui sotto si combinano in AND.\r\n @if (supportsFormula()) {\r\n Per una formula usa la modalita\u2019 \u00ABFormula\u00BB, che scrive <code>filterFormula</code>.\r\n }\r\n </p>\r\n }\r\n\r\n @if (supportsFormula() && logicMode() === 'formula') {\r\n <div class=\"fb-field\">\r\n <label class=\"fb-field__label\">Formula di filtro</label>\r\n <fb-formula-editor\r\n [expression]=\"holder().filterFormula || ''\"\r\n usage=\"Condition\"\r\n expectedDataType=\"Boolean\"\r\n ariaLabel=\"Formula di filtro\"\r\n (expressionChange)=\"setFormula($event)\"\r\n >\r\n <p class=\"fb-field__hint\">\r\n Valutata dal motore di regole, <strong>in alternativa</strong> ai filtri: sta nel campo\r\n <code>filterFormula</code>, e la logica dei filtri non si applica.\r\n </p>\r\n </fb-formula-editor>\r\n </div>\r\n }\r\n\r\n @if (identifierNotFilterable()) {\r\n <p class=\"fb-field__hint\">\r\n Su questo oggetto l\u2019identificativo non e\u2019 filtrabile: la chiave e\u2019 composta e porta la propria\r\n forma canonica. Filtra per le colonne della chiave, una per colonna.\r\n </p>\r\n }\r\n\r\n @if (showEmptyWarning()) {\r\n <p\r\n class=\"fb-callout\"\r\n [class.fb-callout--warn]=\"emptyWarningSeverity() === 'warn'\"\r\n [class.fb-callout--error]=\"emptyWarningSeverity() === 'error'\"\r\n >\r\n {{ emptyWarning() }}\r\n </p>\r\n }\r\n\r\n <div class=\"fb-list\">\r\n @for (filter of filters(); track $index) {\r\n <div class=\"fb-list__item\">\r\n <div class=\"fb-list__header\">\r\n <span class=\"fb-list__index\">{{ $index + 1 }}</span>\r\n <span class=\"fb-list__spacer\"></span>\r\n <button\r\n type=\"button\"\r\n class=\"fb-btn fb-btn--ghost fb-btn--icon\"\r\n aria-label=\"Rimuovi il filtro\"\r\n (click)=\"removeFilter($index)\"\r\n >\r\n \u00D7\r\n </button>\r\n </div>\r\n\r\n <!-- Campo, operatore e valore sono una frase sola: si leggono in riga. -->\r\n <div class=\"fb-fields-row\">\r\n <div class=\"fb-field\">\r\n <label class=\"fb-field__label\">Campo</label>\r\n @if (fieldOptions().length) {\r\n <!-- Insieme chiuso: qui non c'e' un catalogo incompleto da compensare. -->\r\n <select\r\n class=\"fb-select\"\r\n [fbValue]=\"filter.field || ''\"\r\n aria-label=\"Campo del filtro\"\r\n (change)=\"setField($index, $any($event.target).value)\"\r\n >\r\n <option value=\"\">\u2014 scegli \u2014</option>\r\n @for (option of fieldOptions(); track option.value) {\r\n <option [value]=\"option.value\">{{ option.label }}</option>\r\n }\r\n </select>\r\n @if (unknownFixedField(filter)) {\r\n <p class=\"fb-field__error\">\r\n \u00AB{{ filter.field }}\u00BB non e\u2019 fra i campi citabili qui (FIELD_UNKNOWN).\r\n </p>\r\n }\r\n } @else {\r\n <fb-field-picker\r\n [value]=\"filter.field\"\r\n [object]=\"object()\"\r\n [usage]=\"usage()\"\r\n placeholder=\"Scrivi o scegli un campo\"\r\n (valueChange)=\"setField($index, $event ?? '')\"\r\n />\r\n }\r\n </div>\r\n\r\n <div class=\"fb-field fb-field--compact\">\r\n <label class=\"fb-field__label\">Operatore</label>\r\n <select\r\n class=\"fb-select\"\r\n [fbValue]=\"filter.operator || ''\"\r\n (change)=\"setOperator($index, $any($event.target).value)\"\r\n >\r\n @for (operator of operators(); track operator.value) {\r\n <option [value]=\"operator.value\">{{ operator.label }}</option>\r\n }\r\n </select>\r\n </div>\r\n\r\n @if (isNullOperator(filter)) {\r\n <div class=\"fb-field\">\r\n <label class=\"fb-field__label\">Esito atteso</label>\r\n <div class=\"fb-filter__modes\">\r\n <button\r\n type=\"button\"\r\n class=\"fb-filter__mode\"\r\n [class.fb-filter__mode--active]=\"nullExpectation(filter)\"\r\n (click)=\"setNullExpectation($index, true)\"\r\n >\r\n \u00E8 vuoto\r\n </button>\r\n <button\r\n type=\"button\"\r\n class=\"fb-filter__mode\"\r\n [class.fb-filter__mode--active]=\"!nullExpectation(filter)\"\r\n (click)=\"setNullExpectation($index, false)\"\r\n >\r\n non \u00E8 vuoto\r\n </button>\r\n </div>\r\n </div>\r\n } @else {\r\n <div class=\"fb-field\">\r\n <label class=\"fb-field__label\">Valore</label>\r\n <fb-value-editor\r\n [value]=\"filter.value\"\r\n [dataType]=\"fieldDataType(filter)\"\r\n [objectType]=\"fieldObjectType(filter)\"\r\n [valueSet]=\"fieldValueSet(filter)\"\r\n label=\"Valore del filtro\"\r\n [allowFormula]=\"false\"\r\n (valueChange)=\"setValue($index, $event)\"\r\n />\r\n </div>\r\n }\r\n </div>\r\n </div>\r\n } @empty {\r\n <p class=\"fb-empty\">Nessun filtro.</p>\r\n }\r\n </div>\r\n\r\n <button type=\"button\" class=\"fb-btn\" (click)=\"addFilter()\">Aggiungi filtro</button>\r\n</fieldset>\r\n", styles: [":host{display:block}.fb-filter__modes{margin-bottom:8px}.fb-field .fb-filter__modes{margin-bottom:0}\n"], dependencies: [{ kind: "component", type: FieldPickerComponent, selector: "fb-field-picker", inputs: ["value", "object", "usage", "label", "placeholder", "disabled"], outputs: ["valueChange"] }, { kind: "component", type: FormulaEditorComponent, selector: "fb-formula-editor", inputs: ["expression", "usage", "expectedDataType", "scale", "placeholder", "ariaLabel", "disabled", "rows", "preferredRoots", "commitOn"], outputs: ["expressionChange"] }, { kind: "component", type: ValueEditorComponent, selector: "fb-value-editor", inputs: ["value", "label", "dataType", "objectType", "isCollection", "valueSet", "disabled", "allowFormula", "preferredRoots"], outputs: ["valueChange"] }, { kind: "directive", type: SelectValueDirective, selector: "select[fbValue]", inputs: ["fbValue"] }], changeDetection: i0.ChangeDetectionStrategy.OnPush });
10440
+ static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "20.3.31", type: RecordFilterEditorComponent, isStandalone: true, selector: "fb-record-filter-editor", inputs: { holder: { classPropertyName: "holder", publicName: "holder", isSignal: true, isRequired: true, transformFunction: null }, object: { classPropertyName: "object", publicName: "object", isSignal: true, isRequired: false, transformFunction: null }, title: { classPropertyName: "title", publicName: "title", isSignal: true, isRequired: false, transformFunction: null }, usage: { classPropertyName: "usage", publicName: "usage", isSignal: true, isRequired: false, transformFunction: null }, fieldOptions: { classPropertyName: "fieldOptions", publicName: "fieldOptions", isSignal: true, isRequired: false, transformFunction: null }, supportsLogic: { classPropertyName: "supportsLogic", publicName: "supportsLogic", isSignal: true, isRequired: false, transformFunction: null }, supportsFormula: { classPropertyName: "supportsFormula", publicName: "supportsFormula", isSignal: true, isRequired: false, transformFunction: null }, emptyWarning: { classPropertyName: "emptyWarning", publicName: "emptyWarning", isSignal: true, isRequired: false, transformFunction: null }, emptyWarningSeverity: { classPropertyName: "emptyWarningSeverity", publicName: "emptyWarningSeverity", isSignal: true, isRequired: false, transformFunction: null } }, outputs: { changed: "changed" }, ngImport: i0, template: "<fieldset class=\"fb-section\">\r\n <legend class=\"fb-section__title\">{{ title() }}</legend>\r\n\r\n @if (!object() && !fieldOptions().length) {\r\n <p class=\"fb-field__hint\">Scegli prima un oggetto per poter filtrare sui suoi campi.</p>\r\n }\r\n\r\n @if (supportsLogic()) {\r\n <div class=\"fb-filter__modes\" role=\"group\" aria-label=\"Logica dei filtri\">\r\n <button\r\n type=\"button\"\r\n class=\"fb-filter__mode\"\r\n [class.fb-filter__mode--active]=\"logicMode() === 'and'\"\r\n (click)=\"setLogicMode('and')\"\r\n >\r\n Tutti (AND)\r\n </button>\r\n <button\r\n type=\"button\"\r\n class=\"fb-filter__mode\"\r\n [class.fb-filter__mode--active]=\"logicMode() === 'or'\"\r\n (click)=\"setLogicMode('or')\"\r\n >\r\n Almeno uno (OR)\r\n </button>\r\n <button\r\n type=\"button\"\r\n class=\"fb-filter__mode\"\r\n [class.fb-filter__mode--active]=\"logicMode() === 'custom'\"\r\n (click)=\"setLogicMode('custom')\"\r\n >\r\n Espressione\r\n </button>\r\n @if (supportsFormula()) {\r\n <button\r\n type=\"button\"\r\n class=\"fb-filter__mode\"\r\n [class.fb-filter__mode--active]=\"logicMode() === 'formula'\"\r\n (click)=\"setLogicMode('formula')\"\r\n >\r\n Formula\r\n </button>\r\n }\r\n </div>\r\n } @else {\r\n <!-- Qui il modello non ha `filterLogic`: mostrarlo lo farebbe perdere al salvataggio. -->\r\n <p class=\"fb-field__hint\">Su questo elemento i filtri sono sempre combinati in AND.</p>\r\n }\r\n\r\n @if (supportsLogic() && logicMode() === 'custom') {\r\n <div class=\"fb-field\">\r\n <input\r\n class=\"fb-input fb-input--mono\"\r\n [class.fb-input--invalid]=\"!!customLogicError()\"\r\n [value]=\"customLogic()\"\r\n placeholder=\"1 AND (2 OR 3)\"\r\n aria-label=\"Espressione sugli indici dei filtri\"\r\n (input)=\"setCustomLogic($any($event.target).value)\"\r\n />\r\n <!--\r\n Gli indici sono quelli numerati nell\u2019elenco qui sotto, e sono 1-based. Un indice\r\n inesistente e\u2019 `CONDITION_LOGIC_INVALID`, che e\u2019 un **errore**: blocca l\u2019attivazione,\r\n quindi conviene dirlo mentre si scrive e non alla validazione.\r\n -->\r\n @if (customLogicError()) {\r\n <p class=\"fb-field__error\">{{ customLogicError() }}</p>\r\n } @else {\r\n <p class=\"fb-field__hint\">\r\n Gli indici sono 1-based e si riferiscono ai filtri numerati sotto. Cancellare un filtro\r\n riscrive l\u2019espressione automaticamente.\r\n </p>\r\n }\r\n </div>\r\n }\r\n\r\n <!--\r\n `filterLogic: \"Formula\"` non e\u2019 supportato da nessun elemento (\u00A74.4): non lo scrive nessun\r\n gesto di questo editor, ma un flow scritto altrove puo\u2019 portarcelo. Si dice invece di\r\n correggerlo di soppiatto \u2014 e\u2019 l\u2019unico indizio di cosa quel flow voleva fare.\r\n -->\r\n @if (unsupportedFormulaLogic()) {\r\n <p class=\"fb-callout fb-callout--error\">\r\n <code>filterLogic: \"Formula\"</code> non e\u2019 un valore supportato: sui choice set e\u2019 un errore\r\n di validazione, altrove fa fallire l\u2019esecuzione. I filtri qui sotto si combinano in AND.\r\n @if (supportsFormula()) {\r\n Per una formula usa la modalita\u2019 \u00ABFormula\u00BB, che scrive <code>filterFormula</code>.\r\n }\r\n </p>\r\n }\r\n\r\n @if (supportsFormula() && logicMode() === 'formula') {\r\n <div class=\"fb-field\">\r\n <label class=\"fb-field__label\">Formula di filtro</label>\r\n <fb-formula-editor\r\n [expression]=\"holder().filterFormula || ''\"\r\n usage=\"Condition\"\r\n expectedDataType=\"Boolean\"\r\n ariaLabel=\"Formula di filtro\"\r\n (expressionChange)=\"setFormula($event)\"\r\n >\r\n <p class=\"fb-field__hint\">\r\n Valutata dal motore di regole, <strong>in alternativa</strong> ai filtri: sta nel campo\r\n <code>filterFormula</code>, e la logica dei filtri non si applica.\r\n </p>\r\n </fb-formula-editor>\r\n </div>\r\n }\r\n\r\n @if (identifierNotFilterable()) {\r\n <p class=\"fb-field__hint\">\r\n Su questo oggetto l\u2019identificativo non e\u2019 filtrabile: la chiave e\u2019 composta e porta la propria\r\n forma canonica. Filtra per le colonne della chiave, una per colonna.\r\n </p>\r\n }\r\n\r\n @if (showEmptyWarning()) {\r\n <p\r\n class=\"fb-callout\"\r\n [class.fb-callout--warn]=\"emptyWarningSeverity() === 'warn'\"\r\n [class.fb-callout--error]=\"emptyWarningSeverity() === 'error'\"\r\n >\r\n {{ emptyWarning() }}\r\n </p>\r\n }\r\n\r\n <div class=\"fb-list\">\r\n @for (filter of filters(); track $index) {\r\n <div class=\"fb-list__item\">\r\n <div class=\"fb-list__header\">\r\n <span class=\"fb-list__index\">{{ $index + 1 }}</span>\r\n <span class=\"fb-list__spacer\"></span>\r\n <button\r\n type=\"button\"\r\n class=\"fb-btn fb-btn--ghost fb-btn--icon\"\r\n aria-label=\"Rimuovi il filtro\"\r\n (click)=\"removeFilter($index)\"\r\n >\r\n \u00D7\r\n </button>\r\n </div>\r\n\r\n <!-- Campo, operatore e valore sono una frase sola: si leggono in riga. -->\r\n <div class=\"fb-fields-row\">\r\n <div class=\"fb-field\">\r\n <label class=\"fb-field__label\">Campo</label>\r\n @if (fieldOptions().length) {\r\n <!-- Insieme chiuso: qui non c'e' un catalogo incompleto da compensare. -->\r\n <select\r\n class=\"fb-select\"\r\n [fbValue]=\"filter.field || ''\"\r\n aria-label=\"Campo del filtro\"\r\n (change)=\"setField($index, $any($event.target).value)\"\r\n >\r\n <option value=\"\">\u2014 scegli \u2014</option>\r\n @for (option of fieldOptions(); track option.value) {\r\n <option [value]=\"option.value\">{{ option.label }}</option>\r\n }\r\n </select>\r\n @if (unknownFixedField(filter)) {\r\n <p class=\"fb-field__error\">\r\n \u00AB{{ filter.field }}\u00BB non e\u2019 fra i campi citabili qui (FIELD_UNKNOWN).\r\n </p>\r\n }\r\n } @else {\r\n <fb-field-picker\r\n [value]=\"filter.field\"\r\n [object]=\"object()\"\r\n [usage]=\"usage()\"\r\n placeholder=\"Scrivi o scegli un campo\"\r\n (valueChange)=\"setField($index, $event ?? '')\"\r\n />\r\n }\r\n </div>\r\n\r\n <div class=\"fb-field fb-field--compact\">\r\n <label class=\"fb-field__label\">Operatore</label>\r\n <select\r\n class=\"fb-select\"\r\n [fbValue]=\"filter.operator || ''\"\r\n (change)=\"setOperator($index, $any($event.target).value)\"\r\n >\r\n @for (operator of operators(); track operator.value) {\r\n <option [value]=\"operator.value\">{{ operator.label }}</option>\r\n }\r\n </select>\r\n <!--\r\n \u00A77 \u2014 un filtro che legge i valori di prima del record vale quanto l'operazione che\r\n innesca il flow: mai = l'interview fallisce, a volte = funziona a meta'.\r\n -->\r\n @if (priorValuesSeverityOf(filter) === 'error') {\r\n <p class=\"fb-field__error\">{{ priorValuesMessage() }}</p>\r\n } @else if (priorValuesSeverityOf(filter) === 'warning') {\r\n <p class=\"fb-field__hint fb-field__hint--warn\">{{ priorValuesMessage() }}</p>\r\n }\r\n </div>\r\n\r\n @if (isNullOperator(filter)) {\r\n <div class=\"fb-field\">\r\n <label class=\"fb-field__label\">Esito atteso</label>\r\n <div class=\"fb-filter__modes\">\r\n <button\r\n type=\"button\"\r\n class=\"fb-filter__mode\"\r\n [class.fb-filter__mode--active]=\"nullExpectation(filter)\"\r\n (click)=\"setNullExpectation($index, true)\"\r\n >\r\n \u00E8 vuoto\r\n </button>\r\n <button\r\n type=\"button\"\r\n class=\"fb-filter__mode\"\r\n [class.fb-filter__mode--active]=\"!nullExpectation(filter)\"\r\n (click)=\"setNullExpectation($index, false)\"\r\n >\r\n non \u00E8 vuoto\r\n </button>\r\n </div>\r\n </div>\r\n } @else {\r\n <div class=\"fb-field\">\r\n <label class=\"fb-field__label\">Valore</label>\r\n <fb-value-editor\r\n [value]=\"filter.value\"\r\n [dataType]=\"fieldDataType(filter)\"\r\n [objectType]=\"fieldObjectType(filter)\"\r\n [valueSet]=\"fieldValueSet(filter)\"\r\n label=\"Valore del filtro\"\r\n [allowFormula]=\"false\"\r\n (valueChange)=\"setValue($index, $event)\"\r\n />\r\n </div>\r\n }\r\n </div>\r\n </div>\r\n } @empty {\r\n <p class=\"fb-empty\">Nessun filtro.</p>\r\n }\r\n </div>\r\n\r\n <button type=\"button\" class=\"fb-btn\" (click)=\"addFilter()\">Aggiungi filtro</button>\r\n</fieldset>\r\n", styles: [":host{display:block}.fb-filter__modes{margin-bottom:8px}.fb-field .fb-filter__modes{margin-bottom:0}\n"], dependencies: [{ kind: "component", type: FieldPickerComponent, selector: "fb-field-picker", inputs: ["value", "object", "usage", "label", "placeholder", "disabled"], outputs: ["valueChange"] }, { kind: "component", type: FormulaEditorComponent, selector: "fb-formula-editor", inputs: ["expression", "usage", "expectedDataType", "scale", "placeholder", "ariaLabel", "disabled", "rows", "preferredRoots", "commitOn"], outputs: ["expressionChange"] }, { kind: "component", type: ValueEditorComponent, selector: "fb-value-editor", inputs: ["value", "label", "dataType", "objectType", "isCollection", "valueSet", "disabled", "allowFormula", "preferredRoots"], outputs: ["valueChange"] }, { kind: "directive", type: SelectValueDirective, selector: "select[fbValue]", inputs: ["fbValue"] }], changeDetection: i0.ChangeDetectionStrategy.OnPush });
10119
10441
  }
10120
10442
  i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.31", ngImport: i0, type: RecordFilterEditorComponent, decorators: [{
10121
10443
  type: Component,
10122
- args: [{ selector: 'fb-record-filter-editor', standalone: true, imports: [FieldPickerComponent, FormulaEditorComponent, ValueEditorComponent, SelectValueDirective], changeDetection: ChangeDetectionStrategy.OnPush, template: "<fieldset class=\"fb-section\">\r\n <legend class=\"fb-section__title\">{{ title() }}</legend>\r\n\r\n @if (!object() && !fieldOptions().length) {\r\n <p class=\"fb-field__hint\">Scegli prima un oggetto per poter filtrare sui suoi campi.</p>\r\n }\r\n\r\n @if (supportsLogic()) {\r\n <div class=\"fb-filter__modes\" role=\"group\" aria-label=\"Logica dei filtri\">\r\n <button\r\n type=\"button\"\r\n class=\"fb-filter__mode\"\r\n [class.fb-filter__mode--active]=\"logicMode() === 'and'\"\r\n (click)=\"setLogicMode('and')\"\r\n >\r\n Tutti (AND)\r\n </button>\r\n <button\r\n type=\"button\"\r\n class=\"fb-filter__mode\"\r\n [class.fb-filter__mode--active]=\"logicMode() === 'or'\"\r\n (click)=\"setLogicMode('or')\"\r\n >\r\n Almeno uno (OR)\r\n </button>\r\n <button\r\n type=\"button\"\r\n class=\"fb-filter__mode\"\r\n [class.fb-filter__mode--active]=\"logicMode() === 'custom'\"\r\n (click)=\"setLogicMode('custom')\"\r\n >\r\n Espressione\r\n </button>\r\n @if (supportsFormula()) {\r\n <button\r\n type=\"button\"\r\n class=\"fb-filter__mode\"\r\n [class.fb-filter__mode--active]=\"logicMode() === 'formula'\"\r\n (click)=\"setLogicMode('formula')\"\r\n >\r\n Formula\r\n </button>\r\n }\r\n </div>\r\n } @else {\r\n <!-- Qui il modello non ha `filterLogic`: mostrarlo lo farebbe perdere al salvataggio. -->\r\n <p class=\"fb-field__hint\">Su questo elemento i filtri sono sempre combinati in AND.</p>\r\n }\r\n\r\n @if (supportsLogic() && logicMode() === 'custom') {\r\n <div class=\"fb-field\">\r\n <input\r\n class=\"fb-input fb-input--mono\"\r\n [class.fb-input--invalid]=\"!!customLogicError()\"\r\n [value]=\"customLogic()\"\r\n placeholder=\"1 AND (2 OR 3)\"\r\n aria-label=\"Espressione sugli indici dei filtri\"\r\n (input)=\"setCustomLogic($any($event.target).value)\"\r\n />\r\n <!--\r\n Gli indici sono quelli numerati nell\u2019elenco qui sotto, e sono 1-based. Un indice\r\n inesistente e\u2019 `CONDITION_LOGIC_INVALID`, che e\u2019 un **errore**: blocca l\u2019attivazione,\r\n quindi conviene dirlo mentre si scrive e non alla validazione.\r\n -->\r\n @if (customLogicError()) {\r\n <p class=\"fb-field__error\">{{ customLogicError() }}</p>\r\n } @else {\r\n <p class=\"fb-field__hint\">\r\n Gli indici sono 1-based e si riferiscono ai filtri numerati sotto. Cancellare un filtro\r\n riscrive l\u2019espressione automaticamente.\r\n </p>\r\n }\r\n </div>\r\n }\r\n\r\n <!--\r\n `filterLogic: \"Formula\"` non e\u2019 supportato da nessun elemento (\u00A74.4): non lo scrive nessun\r\n gesto di questo editor, ma un flow scritto altrove puo\u2019 portarcelo. Si dice invece di\r\n correggerlo di soppiatto \u2014 e\u2019 l\u2019unico indizio di cosa quel flow voleva fare.\r\n -->\r\n @if (unsupportedFormulaLogic()) {\r\n <p class=\"fb-callout fb-callout--error\">\r\n <code>filterLogic: \"Formula\"</code> non e\u2019 un valore supportato: sui choice set e\u2019 un errore\r\n di validazione, altrove fa fallire l\u2019esecuzione. I filtri qui sotto si combinano in AND.\r\n @if (supportsFormula()) {\r\n Per una formula usa la modalita\u2019 \u00ABFormula\u00BB, che scrive <code>filterFormula</code>.\r\n }\r\n </p>\r\n }\r\n\r\n @if (supportsFormula() && logicMode() === 'formula') {\r\n <div class=\"fb-field\">\r\n <label class=\"fb-field__label\">Formula di filtro</label>\r\n <fb-formula-editor\r\n [expression]=\"holder().filterFormula || ''\"\r\n usage=\"Condition\"\r\n expectedDataType=\"Boolean\"\r\n ariaLabel=\"Formula di filtro\"\r\n (expressionChange)=\"setFormula($event)\"\r\n >\r\n <p class=\"fb-field__hint\">\r\n Valutata dal motore di regole, <strong>in alternativa</strong> ai filtri: sta nel campo\r\n <code>filterFormula</code>, e la logica dei filtri non si applica.\r\n </p>\r\n </fb-formula-editor>\r\n </div>\r\n }\r\n\r\n @if (identifierNotFilterable()) {\r\n <p class=\"fb-field__hint\">\r\n Su questo oggetto l\u2019identificativo non e\u2019 filtrabile: la chiave e\u2019 composta e porta la propria\r\n forma canonica. Filtra per le colonne della chiave, una per colonna.\r\n </p>\r\n }\r\n\r\n @if (showEmptyWarning()) {\r\n <p\r\n class=\"fb-callout\"\r\n [class.fb-callout--warn]=\"emptyWarningSeverity() === 'warn'\"\r\n [class.fb-callout--error]=\"emptyWarningSeverity() === 'error'\"\r\n >\r\n {{ emptyWarning() }}\r\n </p>\r\n }\r\n\r\n <div class=\"fb-list\">\r\n @for (filter of filters(); track $index) {\r\n <div class=\"fb-list__item\">\r\n <div class=\"fb-list__header\">\r\n <span class=\"fb-list__index\">{{ $index + 1 }}</span>\r\n <span class=\"fb-list__spacer\"></span>\r\n <button\r\n type=\"button\"\r\n class=\"fb-btn fb-btn--ghost fb-btn--icon\"\r\n aria-label=\"Rimuovi il filtro\"\r\n (click)=\"removeFilter($index)\"\r\n >\r\n \u00D7\r\n </button>\r\n </div>\r\n\r\n <!-- Campo, operatore e valore sono una frase sola: si leggono in riga. -->\r\n <div class=\"fb-fields-row\">\r\n <div class=\"fb-field\">\r\n <label class=\"fb-field__label\">Campo</label>\r\n @if (fieldOptions().length) {\r\n <!-- Insieme chiuso: qui non c'e' un catalogo incompleto da compensare. -->\r\n <select\r\n class=\"fb-select\"\r\n [fbValue]=\"filter.field || ''\"\r\n aria-label=\"Campo del filtro\"\r\n (change)=\"setField($index, $any($event.target).value)\"\r\n >\r\n <option value=\"\">\u2014 scegli \u2014</option>\r\n @for (option of fieldOptions(); track option.value) {\r\n <option [value]=\"option.value\">{{ option.label }}</option>\r\n }\r\n </select>\r\n @if (unknownFixedField(filter)) {\r\n <p class=\"fb-field__error\">\r\n \u00AB{{ filter.field }}\u00BB non e\u2019 fra i campi citabili qui (FIELD_UNKNOWN).\r\n </p>\r\n }\r\n } @else {\r\n <fb-field-picker\r\n [value]=\"filter.field\"\r\n [object]=\"object()\"\r\n [usage]=\"usage()\"\r\n placeholder=\"Scrivi o scegli un campo\"\r\n (valueChange)=\"setField($index, $event ?? '')\"\r\n />\r\n }\r\n </div>\r\n\r\n <div class=\"fb-field fb-field--compact\">\r\n <label class=\"fb-field__label\">Operatore</label>\r\n <select\r\n class=\"fb-select\"\r\n [fbValue]=\"filter.operator || ''\"\r\n (change)=\"setOperator($index, $any($event.target).value)\"\r\n >\r\n @for (operator of operators(); track operator.value) {\r\n <option [value]=\"operator.value\">{{ operator.label }}</option>\r\n }\r\n </select>\r\n </div>\r\n\r\n @if (isNullOperator(filter)) {\r\n <div class=\"fb-field\">\r\n <label class=\"fb-field__label\">Esito atteso</label>\r\n <div class=\"fb-filter__modes\">\r\n <button\r\n type=\"button\"\r\n class=\"fb-filter__mode\"\r\n [class.fb-filter__mode--active]=\"nullExpectation(filter)\"\r\n (click)=\"setNullExpectation($index, true)\"\r\n >\r\n \u00E8 vuoto\r\n </button>\r\n <button\r\n type=\"button\"\r\n class=\"fb-filter__mode\"\r\n [class.fb-filter__mode--active]=\"!nullExpectation(filter)\"\r\n (click)=\"setNullExpectation($index, false)\"\r\n >\r\n non \u00E8 vuoto\r\n </button>\r\n </div>\r\n </div>\r\n } @else {\r\n <div class=\"fb-field\">\r\n <label class=\"fb-field__label\">Valore</label>\r\n <fb-value-editor\r\n [value]=\"filter.value\"\r\n [dataType]=\"fieldDataType(filter)\"\r\n [objectType]=\"fieldObjectType(filter)\"\r\n [valueSet]=\"fieldValueSet(filter)\"\r\n label=\"Valore del filtro\"\r\n [allowFormula]=\"false\"\r\n (valueChange)=\"setValue($index, $event)\"\r\n />\r\n </div>\r\n }\r\n </div>\r\n </div>\r\n } @empty {\r\n <p class=\"fb-empty\">Nessun filtro.</p>\r\n }\r\n </div>\r\n\r\n <button type=\"button\" class=\"fb-btn\" (click)=\"addFilter()\">Aggiungi filtro</button>\r\n</fieldset>\r\n", styles: [":host{display:block}.fb-filter__modes{margin-bottom:8px}.fb-field .fb-filter__modes{margin-bottom:0}\n"] }]
10444
+ args: [{ selector: 'fb-record-filter-editor', standalone: true, imports: [FieldPickerComponent, FormulaEditorComponent, ValueEditorComponent, SelectValueDirective], changeDetection: ChangeDetectionStrategy.OnPush, template: "<fieldset class=\"fb-section\">\r\n <legend class=\"fb-section__title\">{{ title() }}</legend>\r\n\r\n @if (!object() && !fieldOptions().length) {\r\n <p class=\"fb-field__hint\">Scegli prima un oggetto per poter filtrare sui suoi campi.</p>\r\n }\r\n\r\n @if (supportsLogic()) {\r\n <div class=\"fb-filter__modes\" role=\"group\" aria-label=\"Logica dei filtri\">\r\n <button\r\n type=\"button\"\r\n class=\"fb-filter__mode\"\r\n [class.fb-filter__mode--active]=\"logicMode() === 'and'\"\r\n (click)=\"setLogicMode('and')\"\r\n >\r\n Tutti (AND)\r\n </button>\r\n <button\r\n type=\"button\"\r\n class=\"fb-filter__mode\"\r\n [class.fb-filter__mode--active]=\"logicMode() === 'or'\"\r\n (click)=\"setLogicMode('or')\"\r\n >\r\n Almeno uno (OR)\r\n </button>\r\n <button\r\n type=\"button\"\r\n class=\"fb-filter__mode\"\r\n [class.fb-filter__mode--active]=\"logicMode() === 'custom'\"\r\n (click)=\"setLogicMode('custom')\"\r\n >\r\n Espressione\r\n </button>\r\n @if (supportsFormula()) {\r\n <button\r\n type=\"button\"\r\n class=\"fb-filter__mode\"\r\n [class.fb-filter__mode--active]=\"logicMode() === 'formula'\"\r\n (click)=\"setLogicMode('formula')\"\r\n >\r\n Formula\r\n </button>\r\n }\r\n </div>\r\n } @else {\r\n <!-- Qui il modello non ha `filterLogic`: mostrarlo lo farebbe perdere al salvataggio. -->\r\n <p class=\"fb-field__hint\">Su questo elemento i filtri sono sempre combinati in AND.</p>\r\n }\r\n\r\n @if (supportsLogic() && logicMode() === 'custom') {\r\n <div class=\"fb-field\">\r\n <input\r\n class=\"fb-input fb-input--mono\"\r\n [class.fb-input--invalid]=\"!!customLogicError()\"\r\n [value]=\"customLogic()\"\r\n placeholder=\"1 AND (2 OR 3)\"\r\n aria-label=\"Espressione sugli indici dei filtri\"\r\n (input)=\"setCustomLogic($any($event.target).value)\"\r\n />\r\n <!--\r\n Gli indici sono quelli numerati nell\u2019elenco qui sotto, e sono 1-based. Un indice\r\n inesistente e\u2019 `CONDITION_LOGIC_INVALID`, che e\u2019 un **errore**: blocca l\u2019attivazione,\r\n quindi conviene dirlo mentre si scrive e non alla validazione.\r\n -->\r\n @if (customLogicError()) {\r\n <p class=\"fb-field__error\">{{ customLogicError() }}</p>\r\n } @else {\r\n <p class=\"fb-field__hint\">\r\n Gli indici sono 1-based e si riferiscono ai filtri numerati sotto. Cancellare un filtro\r\n riscrive l\u2019espressione automaticamente.\r\n </p>\r\n }\r\n </div>\r\n }\r\n\r\n <!--\r\n `filterLogic: \"Formula\"` non e\u2019 supportato da nessun elemento (\u00A74.4): non lo scrive nessun\r\n gesto di questo editor, ma un flow scritto altrove puo\u2019 portarcelo. Si dice invece di\r\n correggerlo di soppiatto \u2014 e\u2019 l\u2019unico indizio di cosa quel flow voleva fare.\r\n -->\r\n @if (unsupportedFormulaLogic()) {\r\n <p class=\"fb-callout fb-callout--error\">\r\n <code>filterLogic: \"Formula\"</code> non e\u2019 un valore supportato: sui choice set e\u2019 un errore\r\n di validazione, altrove fa fallire l\u2019esecuzione. I filtri qui sotto si combinano in AND.\r\n @if (supportsFormula()) {\r\n Per una formula usa la modalita\u2019 \u00ABFormula\u00BB, che scrive <code>filterFormula</code>.\r\n }\r\n </p>\r\n }\r\n\r\n @if (supportsFormula() && logicMode() === 'formula') {\r\n <div class=\"fb-field\">\r\n <label class=\"fb-field__label\">Formula di filtro</label>\r\n <fb-formula-editor\r\n [expression]=\"holder().filterFormula || ''\"\r\n usage=\"Condition\"\r\n expectedDataType=\"Boolean\"\r\n ariaLabel=\"Formula di filtro\"\r\n (expressionChange)=\"setFormula($event)\"\r\n >\r\n <p class=\"fb-field__hint\">\r\n Valutata dal motore di regole, <strong>in alternativa</strong> ai filtri: sta nel campo\r\n <code>filterFormula</code>, e la logica dei filtri non si applica.\r\n </p>\r\n </fb-formula-editor>\r\n </div>\r\n }\r\n\r\n @if (identifierNotFilterable()) {\r\n <p class=\"fb-field__hint\">\r\n Su questo oggetto l\u2019identificativo non e\u2019 filtrabile: la chiave e\u2019 composta e porta la propria\r\n forma canonica. Filtra per le colonne della chiave, una per colonna.\r\n </p>\r\n }\r\n\r\n @if (showEmptyWarning()) {\r\n <p\r\n class=\"fb-callout\"\r\n [class.fb-callout--warn]=\"emptyWarningSeverity() === 'warn'\"\r\n [class.fb-callout--error]=\"emptyWarningSeverity() === 'error'\"\r\n >\r\n {{ emptyWarning() }}\r\n </p>\r\n }\r\n\r\n <div class=\"fb-list\">\r\n @for (filter of filters(); track $index) {\r\n <div class=\"fb-list__item\">\r\n <div class=\"fb-list__header\">\r\n <span class=\"fb-list__index\">{{ $index + 1 }}</span>\r\n <span class=\"fb-list__spacer\"></span>\r\n <button\r\n type=\"button\"\r\n class=\"fb-btn fb-btn--ghost fb-btn--icon\"\r\n aria-label=\"Rimuovi il filtro\"\r\n (click)=\"removeFilter($index)\"\r\n >\r\n \u00D7\r\n </button>\r\n </div>\r\n\r\n <!-- Campo, operatore e valore sono una frase sola: si leggono in riga. -->\r\n <div class=\"fb-fields-row\">\r\n <div class=\"fb-field\">\r\n <label class=\"fb-field__label\">Campo</label>\r\n @if (fieldOptions().length) {\r\n <!-- Insieme chiuso: qui non c'e' un catalogo incompleto da compensare. -->\r\n <select\r\n class=\"fb-select\"\r\n [fbValue]=\"filter.field || ''\"\r\n aria-label=\"Campo del filtro\"\r\n (change)=\"setField($index, $any($event.target).value)\"\r\n >\r\n <option value=\"\">\u2014 scegli \u2014</option>\r\n @for (option of fieldOptions(); track option.value) {\r\n <option [value]=\"option.value\">{{ option.label }}</option>\r\n }\r\n </select>\r\n @if (unknownFixedField(filter)) {\r\n <p class=\"fb-field__error\">\r\n \u00AB{{ filter.field }}\u00BB non e\u2019 fra i campi citabili qui (FIELD_UNKNOWN).\r\n </p>\r\n }\r\n } @else {\r\n <fb-field-picker\r\n [value]=\"filter.field\"\r\n [object]=\"object()\"\r\n [usage]=\"usage()\"\r\n placeholder=\"Scrivi o scegli un campo\"\r\n (valueChange)=\"setField($index, $event ?? '')\"\r\n />\r\n }\r\n </div>\r\n\r\n <div class=\"fb-field fb-field--compact\">\r\n <label class=\"fb-field__label\">Operatore</label>\r\n <select\r\n class=\"fb-select\"\r\n [fbValue]=\"filter.operator || ''\"\r\n (change)=\"setOperator($index, $any($event.target).value)\"\r\n >\r\n @for (operator of operators(); track operator.value) {\r\n <option [value]=\"operator.value\">{{ operator.label }}</option>\r\n }\r\n </select>\r\n <!--\r\n \u00A77 \u2014 un filtro che legge i valori di prima del record vale quanto l'operazione che\r\n innesca il flow: mai = l'interview fallisce, a volte = funziona a meta'.\r\n -->\r\n @if (priorValuesSeverityOf(filter) === 'error') {\r\n <p class=\"fb-field__error\">{{ priorValuesMessage() }}</p>\r\n } @else if (priorValuesSeverityOf(filter) === 'warning') {\r\n <p class=\"fb-field__hint fb-field__hint--warn\">{{ priorValuesMessage() }}</p>\r\n }\r\n </div>\r\n\r\n @if (isNullOperator(filter)) {\r\n <div class=\"fb-field\">\r\n <label class=\"fb-field__label\">Esito atteso</label>\r\n <div class=\"fb-filter__modes\">\r\n <button\r\n type=\"button\"\r\n class=\"fb-filter__mode\"\r\n [class.fb-filter__mode--active]=\"nullExpectation(filter)\"\r\n (click)=\"setNullExpectation($index, true)\"\r\n >\r\n \u00E8 vuoto\r\n </button>\r\n <button\r\n type=\"button\"\r\n class=\"fb-filter__mode\"\r\n [class.fb-filter__mode--active]=\"!nullExpectation(filter)\"\r\n (click)=\"setNullExpectation($index, false)\"\r\n >\r\n non \u00E8 vuoto\r\n </button>\r\n </div>\r\n </div>\r\n } @else {\r\n <div class=\"fb-field\">\r\n <label class=\"fb-field__label\">Valore</label>\r\n <fb-value-editor\r\n [value]=\"filter.value\"\r\n [dataType]=\"fieldDataType(filter)\"\r\n [objectType]=\"fieldObjectType(filter)\"\r\n [valueSet]=\"fieldValueSet(filter)\"\r\n label=\"Valore del filtro\"\r\n [allowFormula]=\"false\"\r\n (valueChange)=\"setValue($index, $event)\"\r\n />\r\n </div>\r\n }\r\n </div>\r\n </div>\r\n } @empty {\r\n <p class=\"fb-empty\">Nessun filtro.</p>\r\n }\r\n </div>\r\n\r\n <button type=\"button\" class=\"fb-btn\" (click)=\"addFilter()\">Aggiungi filtro</button>\r\n</fieldset>\r\n", styles: [":host{display:block}.fb-filter__modes{margin-bottom:8px}.fb-field .fb-filter__modes{margin-bottom:0}\n"] }]
10123
10445
  }], ctorParameters: () => [], propDecorators: { holder: [{ type: i0.Input, args: [{ isSignal: true, alias: "holder", required: true }] }], object: [{ type: i0.Input, args: [{ isSignal: true, alias: "object", required: false }] }], title: [{ type: i0.Input, args: [{ isSignal: true, alias: "title", required: false }] }], usage: [{ type: i0.Input, args: [{ isSignal: true, alias: "usage", required: false }] }], fieldOptions: [{ type: i0.Input, args: [{ isSignal: true, alias: "fieldOptions", required: false }] }], supportsLogic: [{ type: i0.Input, args: [{ isSignal: true, alias: "supportsLogic", required: false }] }], supportsFormula: [{ type: i0.Input, args: [{ isSignal: true, alias: "supportsFormula", required: false }] }], emptyWarning: [{ type: i0.Input, args: [{ isSignal: true, alias: "emptyWarning", required: false }] }], emptyWarningSeverity: [{ type: i0.Input, args: [{ isSignal: true, alias: "emptyWarningSeverity", required: false }] }], changed: [{ type: i0.Output, args: ["changed"] }] } });
10124
10446
 
10125
10447
  /**
@@ -14256,9 +14578,20 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.31", ngImpo
14256
14578
  * Con un trigger su record il flow espone `$Record` e `$Record__Prior`; senza, quei
14257
14579
  * riferimenti non sono disponibili e la validazione lo segnala. Il form lo dice, perche' e'
14258
14580
  * la differenza fra un riferimento che funziona e uno che no.
14581
+ *
14582
+ * **Su un trigger da record il «quando parte» non e' una tendina, e' una sequenza** (§3.4). I campi
14583
+ * sono due — `recordTriggerType` e' l'**operazione**, `triggerType` il **momento** rispetto alla
14584
+ * scrittura — ma non sono due assi liberi: l'eliminazione ha un momento solo, e le coppie che
14585
+ * significano qualcosa sono sei. Due tendine indipendenti lascerebbero dichiarare «prima
14586
+ * dell'eliminazione» insieme a «alla creazione»: il runtime la legge come un'eliminazione — con
14587
+ * `RecordBeforeDelete` l'operazione la dice il trigger e `recordTriggerType` non viene guardato —
14588
+ * cioe' il flow parte su qualcosa che nessuno ha scelto. Quindi si chiede l'oggetto, poi
14589
+ * l'operazione, poi i criteri, poi il momento **fra quelli che l'operazione ammette**, e da quella
14590
+ * sequenza la coppia impossibile non e' esprimibile (`START_TRIGGER_MISMATCH` non si incontra mai,
14591
+ * se non aprendo un documento scritto altrove). I momenti li dichiara la voce dell'operazione nei
14592
+ * dizionari, non un elenco cablato: proporre i `triggerTypes` di primo livello significa proporre
14593
+ * coppie che la validazione rifiuta.
14259
14594
  */
14260
- /** I trigger che lavorano su un record e abilitano `$Record`. */
14261
- const RECORD_TRIGGERS = new Set(['RecordBeforeSave', 'RecordAfterSave', 'RecordBeforeDelete']);
14262
14595
  class StartInspectorComponent {
14263
14596
  store = inject(FlowDocumentStore);
14264
14597
  dictionaries = inject(FlowDictionaryStore);
@@ -14303,6 +14636,51 @@ class StartInspectorComponent {
14303
14636
  : '';
14304
14637
  }, ...(ngDevMode ? [{ debugName: "registrationLabel" }] : []));
14305
14638
  recordTriggerTypes = computed(() => this.dictionaries.recordTriggerTypes(), ...(ngDevMode ? [{ debugName: "recordTriggerTypes" }] : []));
14639
+ /**
14640
+ * §3.4 — dove il tipo di flow **e'** innescato da un record, la tendina «quando parte» sparisce:
14641
+ * i suoi tre valori sono il *momento*, e il momento si sceglie per ultimo e fra quelli che
14642
+ * l'operazione ammette. Il ripiego — un documento il cui tipo non si riconosce, dove i trigger
14643
+ * proponibili sono tutti e sei — tiene la tendina e chiede l'operazione accanto: togliere la
14644
+ * scelta a chi ha in mano una coppia fuori dalle sei lo bloccherebbe sul valore che ha.
14645
+ */
14646
+ isRecordFlowType = computed(() => {
14647
+ const proposable = this.triggerTypes();
14648
+ return proposable.length > 0 && proposable.every((entry) => RECORD_TRIGGERS.has(entry.value));
14649
+ }, ...(ngDevMode ? [{ debugName: "isRecordFlowType" }] : []));
14650
+ /** La voce dell'operazione dichiarata dal documento, coi suoi flag (§6.4). */
14651
+ recordOperation = computed(() => this.dictionaries.recordTriggerType(this.start().recordTriggerType), ...(ngDevMode ? [{ debugName: "recordOperation" }] : []));
14652
+ /**
14653
+ * I momenti proponibili: quelli **dell'operazione scelta**. Dove il dizionario non li dichiara —
14654
+ * o l'operazione non e' ancora scelta — si torna ai trigger su record del tipo di flow, che e' il
14655
+ * comportamento di prima: elenco vuoto significa «non lo so», non «nessun momento ammesso».
14656
+ */
14657
+ timingOptions = computed(() => {
14658
+ const declared = this.dictionaries.recordTriggerTimings(this.start().recordTriggerType);
14659
+ return declared.length
14660
+ ? declared
14661
+ : this.triggerTypes().filter((entry) => RECORD_TRIGGERS.has(entry.value));
14662
+ }, ...(ngDevMode ? [{ debugName: "timingOptions" }] : []));
14663
+ /**
14664
+ * Dove l'operazione ammette un momento solo — l'eliminazione — **la domanda non si pone affatto**
14665
+ * e il momento si scrive comunque: il contratto chiede entrambi i campi anche lì. Il controllo
14666
+ * `allowsTimingChoice !== false` e' la cintura: un dizionario che non lo dichiara decide sul
14667
+ * numero di opzioni, che e' la stessa risposta.
14668
+ */
14669
+ showTimingChoice = computed(() => this.recordOperation()?.allowsTimingChoice !== false && this.timingOptions().length > 1, ...(ngDevMode ? [{ debugName: "showTimingChoice" }] : []));
14670
+ /** La voce del momento in mano, per mostrarne l'etichetta dove non c'e' niente da scegliere. */
14671
+ currentTiming = computed(() => this.timingOptions().find((entry) => entry.value === this.triggerType()), ...(ngDevMode ? [{ debugName: "currentTiming" }] : []));
14672
+ /**
14673
+ * §3.4 — `doesRequireRecordChangedToMeetCriteria` ha senso solo dove c'e' un «prima» da
14674
+ * confrontare: alla creazione e all'eliminazione il flag resta inerte, e con esso `IsChanged` non
14675
+ * decide nulla. Flag non dichiarato = lo si mostra: e' il ripiego di sempre.
14676
+ */
14677
+ showRequireChanged = computed(() => this.recordOperation()?.usesPriorRecord !== false, ...(ngDevMode ? [{ debugName: "showRequireChanged" }] : []));
14678
+ /**
14679
+ * La coppia che il documento dichiara non e' una delle sei. Dalla sequenza non e' esprimibile,
14680
+ * quindi arriva da un documento scritto altrove: si **dice**, e la correzione e' un comando.
14681
+ */
14682
+ hasTriggerMismatch = computed(() => this.dictionaries.recordTriggerPair(this.start().recordTriggerType, this.triggerType()) ===
14683
+ 'mismatch', ...(ngDevMode ? [{ debugName: "hasTriggerMismatch" }] : []));
14306
14684
  frequencies = computed(() => this.dictionaries.startFrequencies(), ...(ngDevMode ? [{ debugName: "frequencies" }] : []));
14307
14685
  offsetUnits = computed(() => this.dictionaries.offsetUnits(), ...(ngDevMode ? [{ debugName: "offsetUnits" }] : []));
14308
14686
  triggerType = computed(() => this.start().triggerType ?? 'None', ...(ngDevMode ? [{ debugName: "triggerType" }] : []));
@@ -14316,7 +14694,20 @@ class StartInspectorComponent {
14316
14694
  setTriggerType(value) {
14317
14695
  this.store.updateStart((start) => {
14318
14696
  start.triggerType = value === 'None' ? undefined : value;
14319
- if (!RECORD_TRIGGERS.has(value)) {
14697
+ if (RECORD_TRIGGERS.has(value)) {
14698
+ /*
14699
+ * §3.4 — operazione e momento non sono due assi liberi. Qui il momento si sceglie per primo
14700
+ * (e' il ripiego: dalla sequenza si passa da `setRecordOperation`), quindi se l'operazione
14701
+ * in mano non ammette questo momento va rimessa a posto — all'unica che lo ammette dove ce
14702
+ * n'e' una sola (l'eliminazione), altrimenti tolta. Lasciarla sarebbe la coppia che il
14703
+ * runtime legge al contrario di come e' scritta.
14704
+ */
14705
+ const allowed = this.dictionaries.recordTriggerTypesFor(value);
14706
+ if (allowed.length && !allowed.some((entry) => entry.value === start.recordTriggerType)) {
14707
+ start.recordTriggerType = allowed.length === 1 ? allowed[0].value : undefined;
14708
+ }
14709
+ }
14710
+ else {
14320
14711
  // Campi che appartengono ai **soli** trigger su record: tenerli sarebbe rumore che il
14321
14712
  // motore ignora e che sporca il diff.
14322
14713
  delete start.recordTriggerType;
@@ -14363,11 +14754,42 @@ class StartInspectorComponent {
14363
14754
  delete start.filters;
14364
14755
  });
14365
14756
  }
14366
- setRecordTriggerType(value) {
14757
+ /**
14758
+ * §3.4 — l'operazione, che e' la domanda **prima** del momento, e scrive tutti e due i campi in
14759
+ * **una** mutazione: con due, l'annulla riporterebbe indietro il momento lasciando l'operazione
14760
+ * nuova, cioe' una coppia che non e' nessuna delle sei. Il momento si tocca solo se quello in
14761
+ * mano non e' ammesso — cambiare operazione non deve buttare via una scelta ancora valida.
14762
+ */
14763
+ setRecordOperation(value) {
14367
14764
  this.store.updateStart((start) => {
14368
- start.recordTriggerType = value || undefined;
14765
+ if (!value) {
14766
+ delete start.recordTriggerType;
14767
+ return;
14768
+ }
14769
+ start.recordTriggerType = value;
14770
+ const timings = this.dictionaries.recordTriggerTimings(value).map((entry) => entry.value);
14771
+ const fallback = this.dictionaries.defaultTimingOf(value);
14772
+ if (timings.length && !timings.includes(start.triggerType ?? '') && fallback) {
14773
+ start.triggerType = fallback;
14774
+ }
14775
+ // Il flag del «prima» e' inerte dove l'operazione non ha un prima: tenerlo sarebbe una
14776
+ // condizione scritta che il motore non guarda.
14777
+ if (this.dictionaries.recordTriggerType(value)?.usesPriorRecord === false) {
14778
+ delete start.doesRequireRecordChangedToMeetCriteria;
14779
+ }
14369
14780
  });
14370
14781
  }
14782
+ /**
14783
+ * Rimette il momento fra quelli che l'operazione ammette. E' un **comando** e non una correzione
14784
+ * automatica: su un documento scritto altrove non si sa quale meta' della coppia sia quella
14785
+ * voluta, e cambiarne una di soppiatto e' una modifica che nessuno ha chiesto.
14786
+ */
14787
+ useDefaultTiming() {
14788
+ const timing = this.dictionaries.defaultTimingOf(this.start().recordTriggerType);
14789
+ if (timing) {
14790
+ this.setTriggerType(timing);
14791
+ }
14792
+ }
14371
14793
  setRequireChanged(value) {
14372
14794
  this.store.updateStart((start) => {
14373
14795
  if (value) {
@@ -14445,7 +14867,7 @@ class StartInspectorComponent {
14445
14867
  this.store.setConnector('$start', event.outletKey, event.target, event.isGoTo);
14446
14868
  }
14447
14869
  static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.3.31", ngImport: i0, type: StartInspectorComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
14448
- static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "20.3.31", type: StartInspectorComponent, isStandalone: true, selector: "fb-start-inspector", ngImport: i0, template: "@if (!hasEntryPoint()) {\r\n <p class=\"fb-callout fb-callout--error\">\r\n Lo Start non punta a nessun elemento: il flow non ha un punto di ingresso e non e\u2019 eseguibile\r\n (START_NO_ENTRY_POINT).\r\n </p>\r\n}\r\n\r\n<div class=\"fb-field\">\r\n <label class=\"fb-field__label\">Quando parte</label>\r\n <!--\r\n \u00A73.4 \u2014 i trigger sono quelli del **tipo di flow** scelto alla creazione, non tutti: la coppia\r\n `processType` + `triggerType` **e'** il tipo, e cambiarla qui vorrebbe dire cambiarlo.\r\n -->\r\n <select class=\"fb-select\" [fbValue]=\"triggerType()\" (change)=\"setTriggerType($any($event.target).value)\">\r\n @for (trigger of triggerTypes(); track trigger.value) {\r\n <option [value]=\"trigger.value\">{{ trigger.label }}</option>\r\n }\r\n </select>\r\n @if (flowType(); as type) {\r\n <p class=\"fb-field__hint\">\r\n Tipo di flow: <strong>{{ type.label }}</strong\r\n >{{ type.description ? ' \u2014 ' + type.description : '' }}\r\n </p>\r\n }\r\n @if (requiresRegistration()) {\r\n <!--\r\n \u00A78 \u2014 l'unico caso in cui una definizione **valida** puo' non essere attivabile: il flow non\r\n parte da solo, e l'infrastruttura dell'ospite deve prenderlo in carico. Dirlo qui, dove si\r\n sceglie il trigger, e' dirlo prima che l'attivazione fallisca.\r\n -->\r\n <p class=\"fb-callout\">\r\n Questo flow non parte da solo: attivandolo,\r\n {{ registrationLabel() ? 'il servizio \u00AB' + registrationLabel() + '\u00BB' : 'l\u2019infrastruttura dell\u2019ambiente' }}\r\n deve <strong>prenderlo in carico</strong>. Se non c\u2019e\u2019 chi lo faccia, l\u2019attivazione non riesce\r\n anche con la definizione a posto.\r\n </p>\r\n }\r\n</div>\r\n\r\n@if (isRecordTrigger()) {\r\n <p class=\"fb-callout\">\r\n Con un trigger su record il flow espone <code>$Record</code> e <code>$Record__Prior</code>.\r\n </p>\r\n\r\n <div class=\"fb-field\">\r\n <label class=\"fb-field__label fb-field__label--required\">Oggetto che innesca</label>\r\n <fb-object-picker\r\n [value]=\"start().object\"\r\n label=\"Oggetto che innesca\"\r\n placeholder=\"Scrivi o scegli un oggetto\"\r\n (valueChange)=\"setObject($event ?? '')\"\r\n />\r\n @if (!start().object) {\r\n <p class=\"fb-field__error\">Obbligatorio con i trigger su record (START_OBJECT_MISSING).</p>\r\n }\r\n </div>\r\n\r\n <div class=\"fb-field\">\r\n <label class=\"fb-field__label\">Su quale operazione</label>\r\n <select\r\n class=\"fb-select\"\r\n [fbValue]=\"start().recordTriggerType || ''\"\r\n (change)=\"setRecordTriggerType($any($event.target).value)\"\r\n >\r\n <option value=\"\">\u2014 scegli \u2014</option>\r\n @for (type of recordTriggerTypes(); track type.value) {\r\n <option [value]=\"type.value\">{{ type.label }}</option>\r\n }\r\n </select>\r\n </div>\r\n\r\n <fb-record-filter-editor\r\n [holder]=\"$any(start())\"\r\n [object]=\"start().object\"\r\n title=\"Criteri di ingresso\"\r\n usage=\"filterable\"\r\n [supportsLogic]=\"true\"\r\n [supportsFormula]=\"true\"\r\n emptyWarning=\"Senza criteri il flow parte su ogni record.\"\r\n (changed)=\"onFiltersChanged($event)\"\r\n />\r\n\r\n <label class=\"fb-check\">\r\n <input\r\n type=\"checkbox\"\r\n [checked]=\"start().doesRequireRecordChangedToMeetCriteria === true\"\r\n (change)=\"setRequireChanged($any($event.target).checked)\"\r\n />\r\n Solo se il record <em>non</em> soddisfaceva i criteri prima del salvataggio\r\n </label>\r\n}\r\n\r\n@if (isScheduled()) {\r\n <fieldset class=\"fb-section\">\r\n <legend class=\"fb-section__title\">Pianificazione</legend>\r\n <div class=\"fb-field\">\r\n <label class=\"fb-field__label fb-field__label--required\">Frequenza</label>\r\n <select\r\n class=\"fb-select\"\r\n [fbValue]=\"start().schedule?.frequency || ''\"\r\n (change)=\"setFrequency($any($event.target).value)\"\r\n >\r\n <option value=\"\">\u2014 scegli \u2014</option>\r\n @for (frequency of frequencies(); track frequency.value) {\r\n <option [value]=\"frequency.value\">{{ frequency.label }}</option>\r\n }\r\n </select>\r\n </div>\r\n <div class=\"fb-field__row\">\r\n <div class=\"fb-field\">\r\n <label class=\"fb-field__label\">Data di inizio</label>\r\n <input\r\n class=\"fb-input\"\r\n type=\"date\"\r\n [value]=\"startDateValue()\"\r\n (input)=\"setStartDate($any($event.target).value)\"\r\n />\r\n </div>\r\n <div class=\"fb-field\">\r\n <label class=\"fb-field__label\">Ora</label>\r\n <input\r\n class=\"fb-input\"\r\n type=\"time\"\r\n [value]=\"startTimeValue()\"\r\n (input)=\"setStartTime($any($event.target).value)\"\r\n />\r\n </div>\r\n </div>\r\n @if (!start().schedule?.frequency) {\r\n <p class=\"fb-field__error\">La pianificazione e\u2019 obbligatoria con questo trigger (START_SCHEDULE_MISSING).</p>\r\n }\r\n </fieldset>\r\n\r\n <!--\r\n \u00A73.4 \u2014 su uno schedulato l'oggetto e' **facoltativo** e vuol dire un'altra cosa che sui trigger\r\n su record: non \u00ABl'entita' che innesca\u00BB ma \u00ABun'esecuzione per ogni record che soddisfa i filtri\u00BB.\r\n Senza, il flow gira una volta sola.\r\n -->\r\n <fieldset class=\"fb-section\">\r\n <legend class=\"fb-section__title\">Un\u2019esecuzione per record</legend>\r\n <p class=\"fb-section__note\">\r\n Facoltativo. Indicando un\u2019entita\u2019, il flow gira <strong>una volta per ogni record</strong> che\r\n soddisfa i filtri invece di una volta sola.\r\n </p>\r\n <div class=\"fb-field\">\r\n <label class=\"fb-field__label\">Entita\u2019</label>\r\n <fb-object-picker\r\n [value]=\"start().object\"\r\n label=\"Entita\u2019\"\r\n placeholder=\"Nessuna: una sola esecuzione\"\r\n (valueChange)=\"setObject($event ?? '')\"\r\n />\r\n </div>\r\n @if (start().object) {\r\n <fb-record-filter-editor\r\n [holder]=\"$any(start())\"\r\n [object]=\"start().object\"\r\n title=\"Quali record\"\r\n usage=\"filterable\"\r\n [supportsLogic]=\"true\"\r\n [supportsFormula]=\"true\"\r\n emptyWarning=\"Senza criteri il flow gira su ogni record dell\u2019entita\u2019.\"\r\n (changed)=\"onFiltersChanged($event)\"\r\n />\r\n }\r\n </fieldset>\r\n\r\n <fieldset class=\"fb-section\">\r\n <legend class=\"fb-section__title\">Percorsi differiti</legend>\r\n <p class=\"fb-section__note\">\r\n Ogni percorso e\u2019 un ramo che parte dopo un intervallo, calcolato da un istante di riferimento.\r\n </p>\r\n\r\n <div class=\"fb-list\">\r\n @for (path of scheduledPaths(); track $index) {\r\n <div class=\"fb-list__item\">\r\n <div class=\"fb-list__header\">\r\n <span class=\"fb-list__index\">{{ $index + 1 }}</span>\r\n <span class=\"fb-list__spacer\"></span>\r\n <button\r\n type=\"button\"\r\n class=\"fb-btn fb-btn--ghost fb-btn--icon\"\r\n aria-label=\"Rimuovi il percorso\"\r\n (click)=\"removeScheduledPath($index)\"\r\n >\r\n \u00D7\r\n </button>\r\n </div>\r\n\r\n <div class=\"fb-field\">\r\n <label class=\"fb-field__label\">Etichetta</label>\r\n <input\r\n class=\"fb-input\"\r\n [value]=\"path.label || ''\"\r\n (input)=\"setPathField($index, 'label', $any($event.target).value)\"\r\n />\r\n </div>\r\n <div class=\"fb-field\">\r\n <label class=\"fb-field__label fb-field__label--required\">Nome tecnico</label>\r\n <input\r\n class=\"fb-input fb-input--mono\"\r\n [value]=\"path.name || ''\"\r\n (input)=\"setPathField($index, 'name', $any($event.target).value)\"\r\n />\r\n </div>\r\n <div class=\"fb-field__row\">\r\n <div class=\"fb-field\">\r\n <label class=\"fb-field__label\">Dopo</label>\r\n <input\r\n class=\"fb-input\"\r\n type=\"number\"\r\n [value]=\"path.offsetNumber ?? ''\"\r\n (input)=\"setPathOffsetNumber($index, $any($event.target).value)\"\r\n />\r\n </div>\r\n <div class=\"fb-field\">\r\n <label class=\"fb-field__label\">Unita\u2019</label>\r\n <select\r\n class=\"fb-select\"\r\n [fbValue]=\"path.offsetUnit || ''\"\r\n (change)=\"setPathField($index, 'offsetUnit', $any($event.target).value)\"\r\n >\r\n <option value=\"\">\u2014</option>\r\n @for (unit of offsetUnits(); track unit.value) {\r\n <option [value]=\"unit.value\">{{ unit.label }}</option>\r\n }\r\n </select>\r\n </div>\r\n </div>\r\n <div class=\"fb-field\">\r\n <label class=\"fb-field__label\">Istante di riferimento</label>\r\n <input\r\n class=\"fb-input\"\r\n [value]=\"path.timeSource || ''\"\r\n placeholder=\"Es. RecordTriggerEvent\"\r\n (input)=\"setPathField($index, 'timeSource', $any($event.target).value)\"\r\n />\r\n </div>\r\n <div class=\"fb-field\">\r\n <label class=\"fb-field__label\">Campo data del record</label>\r\n <input\r\n class=\"fb-input\"\r\n [value]=\"path.recordField || ''\"\r\n (input)=\"setPathField($index, 'recordField', $any($event.target).value)\"\r\n />\r\n </div>\r\n </div>\r\n } @empty {\r\n <p class=\"fb-empty\">Nessun percorso differito.</p>\r\n }\r\n </div>\r\n <button type=\"button\" class=\"fb-btn\" (click)=\"addScheduledPath()\">Aggiungi percorso</button>\r\n </fieldset>\r\n}\r\n\r\n@if (triggerType() === 'None') {\r\n <p class=\"fb-field__hint\">\r\n Il flow parte solo su richiesta. I valori iniziali si passano nelle variabili di input.\r\n </p>\r\n}\r\n\r\n@if (isEvent()) {\r\n <fieldset class=\"fb-section\">\r\n <legend class=\"fb-section__title\">Evento</legend>\r\n <p class=\"fb-section__note\">\r\n Il flow parte alla ricezione di un evento: e\u2019 a questo che l\u2019host si sottoscrive quando la\r\n versione viene attivata (\u00A78).\r\n </p>\r\n <div class=\"fb-field\">\r\n <label class=\"fb-field__label fb-field__label--required\">Tipo di evento</label>\r\n <input\r\n class=\"fb-input fb-input--mono\"\r\n [value]=\"start().eventType || ''\"\r\n placeholder=\"Es. OrdineEvaso\"\r\n (input)=\"setEventField('eventType', $any($event.target).value)\"\r\n />\r\n @if (!start().eventType) {\r\n <p class=\"fb-field__error\">Obbligatorio con questo trigger (START_EVENT_MISSING).</p>\r\n }\r\n </div>\r\n <div class=\"fb-field\">\r\n <label class=\"fb-field__label\">Nome dell\u2019evento</label>\r\n <input\r\n class=\"fb-input fb-input--mono\"\r\n [value]=\"start().eventName || ''\"\r\n (input)=\"setEventField('eventName', $any($event.target).value)\"\r\n />\r\n <p class=\"fb-field__hint\">Facoltativo: serve dove il tipo da solo non identifica l\u2019evento.</p>\r\n </div>\r\n </fieldset>\r\n}\r\n\r\n<fb-connector-editor\r\n [nodeName]=\"'$start'\"\r\n [node]=\"startNode()\"\r\n [outlets]=\"outlets()\"\r\n title=\"Da dove comincia\"\r\n (connectorChanged)=\"onConnectorChanged($event)\"\r\n/>\r\n", dependencies: [{ kind: "component", type: ConnectorEditorComponent, selector: "fb-connector-editor", inputs: ["nodeName", "node", "outlets", "title"], outputs: ["connectorChanged"] }, { kind: "component", type: ObjectPickerComponent, selector: "fb-object-picker", inputs: ["value", "label", "placeholder", "disabled"], outputs: ["valueChange"] }, { kind: "component", type: RecordFilterEditorComponent, selector: "fb-record-filter-editor", inputs: ["holder", "object", "title", "usage", "fieldOptions", "supportsLogic", "supportsFormula", "emptyWarning", "emptyWarningSeverity"], outputs: ["changed"] }, { kind: "directive", type: SelectValueDirective, selector: "select[fbValue]", inputs: ["fbValue"] }], changeDetection: i0.ChangeDetectionStrategy.OnPush });
14870
+ static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "20.3.31", type: StartInspectorComponent, isStandalone: true, selector: "fb-start-inspector", ngImport: i0, template: "@if (!hasEntryPoint()) {\r\n <p class=\"fb-callout fb-callout--error\">\r\n Lo Start non punta a nessun elemento: il flow non ha un punto di ingresso e non e\u2019 eseguibile\r\n (START_NO_ENTRY_POINT).\r\n </p>\r\n}\r\n\r\n<!--\r\n \u00A73.4 \u2014 su un flow innescato da un record la tendina non c'e': i suoi tre valori sono il *momento*,\r\n e il momento e' l'ultima domanda della sequenza qui sotto, posta fra i soli momenti che\r\n l'operazione scelta ammette. Altrove i trigger sono quelli del **tipo di flow** scelto alla\r\n creazione, non tutti: la coppia `processType` + `triggerType` **e'** il tipo, e cambiarla qui\r\n vorrebbe dire cambiarlo.\r\n-->\r\n@if (!isRecordFlowType()) {\r\n <div class=\"fb-field\">\r\n <label class=\"fb-field__label\">Quando parte</label>\r\n <select class=\"fb-select\" [fbValue]=\"triggerType()\" (change)=\"setTriggerType($any($event.target).value)\">\r\n @for (trigger of triggerTypes(); track trigger.value) {\r\n <option [value]=\"trigger.value\">{{ trigger.label }}</option>\r\n }\r\n </select>\r\n </div>\r\n}\r\n\r\n@if (flowType(); as type) {\r\n <p class=\"fb-field__hint\">\r\n Tipo di flow: <strong>{{ type.label }}</strong\r\n >{{ type.description ? ' \u2014 ' + type.description : '' }}\r\n </p>\r\n}\r\n@if (requiresRegistration()) {\r\n <!--\r\n \u00A78 \u2014 l'unico caso in cui una definizione **valida** puo' non essere attivabile: il flow non\r\n parte da solo, e l'infrastruttura dell'ospite deve prenderlo in carico. Dirlo qui, dove si\r\n sceglie il trigger, e' dirlo prima che l'attivazione fallisca.\r\n -->\r\n <p class=\"fb-callout\">\r\n Questo flow non parte da solo: attivandolo,\r\n {{ registrationLabel() ? 'il servizio \u00AB' + registrationLabel() + '\u00BB' : 'l\u2019infrastruttura dell\u2019ambiente' }}\r\n deve <strong>prenderlo in carico</strong>. Se non c\u2019e\u2019 chi lo faccia, l\u2019attivazione non riesce\r\n anche con la definizione a posto.\r\n </p>\r\n}\r\n\r\n@if (isRecordTrigger()) {\r\n <p class=\"fb-callout\">\r\n Con un trigger su record il flow espone <code>$Record</code> e <code>$Record__Prior</code>.\r\n </p>\r\n\r\n <div class=\"fb-field\">\r\n <label class=\"fb-field__label fb-field__label--required\">Oggetto che innesca</label>\r\n <fb-object-picker\r\n [value]=\"start().object\"\r\n label=\"Oggetto che innesca\"\r\n placeholder=\"Scrivi o scegli un oggetto\"\r\n (valueChange)=\"setObject($event ?? '')\"\r\n />\r\n @if (!start().object) {\r\n <p class=\"fb-field__error\">Obbligatorio con i trigger su record (START_OBJECT_MISSING).</p>\r\n }\r\n </div>\r\n\r\n <!--\r\n \u00A73.4 \u2014 **l'operazione prima, il momento dopo**: sono due campi ma non due assi liberi, e da due\r\n tendine indipendenti si dichiara \u00ABprima dell'eliminazione\u00BB insieme a \u00ABalla creazione\u00BB, che il\r\n runtime legge come un'eliminazione. Qui quella coppia non e' esprimibile.\r\n -->\r\n <div class=\"fb-field\">\r\n <label class=\"fb-field__label fb-field__label--required\">Su quale operazione</label>\r\n <select\r\n class=\"fb-select\"\r\n [fbValue]=\"start().recordTriggerType || ''\"\r\n (change)=\"setRecordOperation($any($event.target).value)\"\r\n >\r\n <option value=\"\">\u2014 scegli \u2014</option>\r\n @for (type of recordTriggerTypes(); track type.value) {\r\n <option [value]=\"type.value\" [title]=\"type.description || type.label\">{{ type.label }}</option>\r\n }\r\n </select>\r\n @if (recordOperation()?.description; as description) {\r\n <p class=\"fb-field__hint\">{{ description }}</p>\r\n }\r\n @if (!start().recordTriggerType) {\r\n <!-- Avviso e non errore: il motore lo legge come \u00ABogni salvataggio\u00BB (\u00A73.4). -->\r\n <p class=\"fb-field__hint fb-field__hint--warn\">\r\n Senza operazione il flow parte su <strong>ogni</strong> salvataggio, creazioni e\r\n aggiornamenti (START_RECORD_TRIGGER_MISSING).\r\n </p>\r\n }\r\n </div>\r\n\r\n <fb-record-filter-editor\r\n [holder]=\"$any(start())\"\r\n [object]=\"start().object\"\r\n title=\"Criteri di ingresso\"\r\n usage=\"filterable\"\r\n [supportsLogic]=\"true\"\r\n [supportsFormula]=\"true\"\r\n emptyWarning=\"Senza criteri il flow parte su ogni record.\"\r\n (changed)=\"onFiltersChanged($event)\"\r\n />\r\n\r\n @if (showRequireChanged()) {\r\n <!--\r\n \u00A73.4 \u2014 il \u00ABprima\u00BB da confrontare c'e' solo dove l'operazione lo porta (`usesPriorRecord`):\r\n alla creazione e all'eliminazione questo flag \u2014 e con lui l'operatore `IsChanged` \u2014 non decide\r\n nulla, quindi la domanda non si pone.\r\n -->\r\n <label class=\"fb-check\">\r\n <input\r\n type=\"checkbox\"\r\n [checked]=\"start().doesRequireRecordChangedToMeetCriteria === true\"\r\n (change)=\"setRequireChanged($any($event.target).checked)\"\r\n />\r\n Solo se il record <em>non</em> soddisfaceva i criteri prima del salvataggio\r\n </label>\r\n } @else if (start().doesRequireRecordChangedToMeetCriteria === true) {\r\n <!--\r\n Il flag c'e' ma l'operazione non ha un \u00ABprima\u00BB: arriva da un documento scritto altrove, o da\r\n un'operazione cambiata prima di questa versione dell'editor. Nasconderlo e basta lo\r\n toglierebbe dalla vista lasciandolo scritto \u2014 si dice, e la ripulitura e' un comando.\r\n E' un **avviso** e non un errore, a differenza di una condizione che legge i valori\r\n precedenti: questo il runtime lo ignora invece di fallire (\u00A77).\r\n -->\r\n <p class=\"fb-callout fb-callout--warn\">\r\n Il documento chiede che il record <strong>non</strong> soddisfacesse i criteri prima del\r\n salvataggio, ma con questa operazione non c\u2019e\u2019 un \u00ABprima\u00BB da confrontare: il motore lo ignora\r\n (PRIOR_VALUES_NOT_AVAILABLE).\r\n <button type=\"button\" class=\"fb-btn fb-btn--ghost\" (click)=\"setRequireChanged(false)\">\r\n Togli la condizione\r\n </button>\r\n </p>\r\n }\r\n\r\n <!--\r\n Il momento e' l'**ultima** domanda, e sta qui solo dove la tendina in cima non c'e': nel ripiego\r\n il `triggerType` lo governa quella, e due controlli sullo stesso campo si contraddicono a vista.\r\n -->\r\n @if (isRecordFlowType()) {\r\n <div class=\"fb-field\">\r\n <label class=\"fb-field__label fb-field__label--required\">In che momento</label>\r\n @if (showTimingChoice()) {\r\n <select class=\"fb-select\" [fbValue]=\"triggerType()\" (change)=\"setTriggerType($any($event.target).value)\">\r\n @for (timing of timingOptions(); track timing.value) {\r\n <option [value]=\"timing.value\" [title]=\"timing.description || timing.label\">{{ timing.label }}</option>\r\n }\r\n </select>\r\n @if (currentTiming()?.description; as description) {\r\n <p class=\"fb-field__hint\">{{ description }}</p>\r\n }\r\n } @else {\r\n <!--\r\n Momento unico (`allowsTimingChoice: false`, l'eliminazione): la domanda non si pone, ma il\r\n campo si scrive lo stesso \u2014 il contratto chiede **entrambi**.\r\n -->\r\n <p class=\"fb-field__hint\">\r\n <strong>{{ currentTiming()?.label || triggerType() }}</strong> \u2014 con questa operazione e\u2019\r\n l\u2019unico momento possibile, e viene scritto comunque nel documento.\r\n </p>\r\n }\r\n </div>\r\n }\r\n\r\n @if (hasTriggerMismatch()) {\r\n <p class=\"fb-callout fb-callout--error\">\r\n L\u2019operazione e il momento scritti nel documento non stanno insieme (START_TRIGGER_MISMATCH):\r\n il flow parte su un\u2019operazione diversa da quella scelta, e l\u2019attivazione non riesce.\r\n <button type=\"button\" class=\"fb-btn fb-btn--ghost\" (click)=\"useDefaultTiming()\">\r\n Riporta il momento fra quelli ammessi\r\n </button>\r\n </p>\r\n }\r\n}\r\n\r\n@if (isScheduled()) {\r\n <fieldset class=\"fb-section\">\r\n <legend class=\"fb-section__title\">Pianificazione</legend>\r\n <div class=\"fb-field\">\r\n <label class=\"fb-field__label fb-field__label--required\">Frequenza</label>\r\n <select\r\n class=\"fb-select\"\r\n [fbValue]=\"start().schedule?.frequency || ''\"\r\n (change)=\"setFrequency($any($event.target).value)\"\r\n >\r\n <option value=\"\">\u2014 scegli \u2014</option>\r\n @for (frequency of frequencies(); track frequency.value) {\r\n <option [value]=\"frequency.value\">{{ frequency.label }}</option>\r\n }\r\n </select>\r\n </div>\r\n <div class=\"fb-field__row\">\r\n <div class=\"fb-field\">\r\n <label class=\"fb-field__label\">Data di inizio</label>\r\n <input\r\n class=\"fb-input\"\r\n type=\"date\"\r\n [value]=\"startDateValue()\"\r\n (input)=\"setStartDate($any($event.target).value)\"\r\n />\r\n </div>\r\n <div class=\"fb-field\">\r\n <label class=\"fb-field__label\">Ora</label>\r\n <input\r\n class=\"fb-input\"\r\n type=\"time\"\r\n [value]=\"startTimeValue()\"\r\n (input)=\"setStartTime($any($event.target).value)\"\r\n />\r\n </div>\r\n </div>\r\n @if (!start().schedule?.frequency) {\r\n <p class=\"fb-field__error\">La pianificazione e\u2019 obbligatoria con questo trigger (START_SCHEDULE_MISSING).</p>\r\n }\r\n </fieldset>\r\n\r\n <!--\r\n \u00A73.4 \u2014 su uno schedulato l'oggetto e' **facoltativo** e vuol dire un'altra cosa che sui trigger\r\n su record: non \u00ABl'entita' che innesca\u00BB ma \u00ABun'esecuzione per ogni record che soddisfa i filtri\u00BB.\r\n Senza, il flow gira una volta sola.\r\n -->\r\n <fieldset class=\"fb-section\">\r\n <legend class=\"fb-section__title\">Un\u2019esecuzione per record</legend>\r\n <p class=\"fb-section__note\">\r\n Facoltativo. Indicando un\u2019entita\u2019, il flow gira <strong>una volta per ogni record</strong> che\r\n soddisfa i filtri invece di una volta sola.\r\n </p>\r\n <div class=\"fb-field\">\r\n <label class=\"fb-field__label\">Entita\u2019</label>\r\n <fb-object-picker\r\n [value]=\"start().object\"\r\n label=\"Entita\u2019\"\r\n placeholder=\"Nessuna: una sola esecuzione\"\r\n (valueChange)=\"setObject($event ?? '')\"\r\n />\r\n </div>\r\n @if (start().object) {\r\n <fb-record-filter-editor\r\n [holder]=\"$any(start())\"\r\n [object]=\"start().object\"\r\n title=\"Quali record\"\r\n usage=\"filterable\"\r\n [supportsLogic]=\"true\"\r\n [supportsFormula]=\"true\"\r\n emptyWarning=\"Senza criteri il flow gira su ogni record dell\u2019entita\u2019.\"\r\n (changed)=\"onFiltersChanged($event)\"\r\n />\r\n }\r\n </fieldset>\r\n\r\n <fieldset class=\"fb-section\">\r\n <legend class=\"fb-section__title\">Percorsi differiti</legend>\r\n <p class=\"fb-section__note\">\r\n Ogni percorso e\u2019 un ramo che parte dopo un intervallo, calcolato da un istante di riferimento.\r\n </p>\r\n\r\n <div class=\"fb-list\">\r\n @for (path of scheduledPaths(); track $index) {\r\n <div class=\"fb-list__item\">\r\n <div class=\"fb-list__header\">\r\n <span class=\"fb-list__index\">{{ $index + 1 }}</span>\r\n <span class=\"fb-list__spacer\"></span>\r\n <button\r\n type=\"button\"\r\n class=\"fb-btn fb-btn--ghost fb-btn--icon\"\r\n aria-label=\"Rimuovi il percorso\"\r\n (click)=\"removeScheduledPath($index)\"\r\n >\r\n \u00D7\r\n </button>\r\n </div>\r\n\r\n <div class=\"fb-field\">\r\n <label class=\"fb-field__label\">Etichetta</label>\r\n <input\r\n class=\"fb-input\"\r\n [value]=\"path.label || ''\"\r\n (input)=\"setPathField($index, 'label', $any($event.target).value)\"\r\n />\r\n </div>\r\n <div class=\"fb-field\">\r\n <label class=\"fb-field__label fb-field__label--required\">Nome tecnico</label>\r\n <input\r\n class=\"fb-input fb-input--mono\"\r\n [value]=\"path.name || ''\"\r\n (input)=\"setPathField($index, 'name', $any($event.target).value)\"\r\n />\r\n </div>\r\n <div class=\"fb-field__row\">\r\n <div class=\"fb-field\">\r\n <label class=\"fb-field__label\">Dopo</label>\r\n <input\r\n class=\"fb-input\"\r\n type=\"number\"\r\n [value]=\"path.offsetNumber ?? ''\"\r\n (input)=\"setPathOffsetNumber($index, $any($event.target).value)\"\r\n />\r\n </div>\r\n <div class=\"fb-field\">\r\n <label class=\"fb-field__label\">Unita\u2019</label>\r\n <select\r\n class=\"fb-select\"\r\n [fbValue]=\"path.offsetUnit || ''\"\r\n (change)=\"setPathField($index, 'offsetUnit', $any($event.target).value)\"\r\n >\r\n <option value=\"\">\u2014</option>\r\n @for (unit of offsetUnits(); track unit.value) {\r\n <option [value]=\"unit.value\">{{ unit.label }}</option>\r\n }\r\n </select>\r\n </div>\r\n </div>\r\n <div class=\"fb-field\">\r\n <label class=\"fb-field__label\">Istante di riferimento</label>\r\n <input\r\n class=\"fb-input\"\r\n [value]=\"path.timeSource || ''\"\r\n placeholder=\"Es. RecordTriggerEvent\"\r\n (input)=\"setPathField($index, 'timeSource', $any($event.target).value)\"\r\n />\r\n </div>\r\n <div class=\"fb-field\">\r\n <label class=\"fb-field__label\">Campo data del record</label>\r\n <input\r\n class=\"fb-input\"\r\n [value]=\"path.recordField || ''\"\r\n (input)=\"setPathField($index, 'recordField', $any($event.target).value)\"\r\n />\r\n </div>\r\n </div>\r\n } @empty {\r\n <p class=\"fb-empty\">Nessun percorso differito.</p>\r\n }\r\n </div>\r\n <button type=\"button\" class=\"fb-btn\" (click)=\"addScheduledPath()\">Aggiungi percorso</button>\r\n </fieldset>\r\n}\r\n\r\n@if (triggerType() === 'None') {\r\n <p class=\"fb-field__hint\">\r\n Il flow parte solo su richiesta. I valori iniziali si passano nelle variabili di input.\r\n </p>\r\n}\r\n\r\n@if (isEvent()) {\r\n <fieldset class=\"fb-section\">\r\n <legend class=\"fb-section__title\">Evento</legend>\r\n <p class=\"fb-section__note\">\r\n Il flow parte alla ricezione di un evento: e\u2019 a questo che l\u2019host si sottoscrive quando la\r\n versione viene attivata (\u00A78).\r\n </p>\r\n <div class=\"fb-field\">\r\n <label class=\"fb-field__label fb-field__label--required\">Tipo di evento</label>\r\n <input\r\n class=\"fb-input fb-input--mono\"\r\n [value]=\"start().eventType || ''\"\r\n placeholder=\"Es. OrdineEvaso\"\r\n (input)=\"setEventField('eventType', $any($event.target).value)\"\r\n />\r\n @if (!start().eventType) {\r\n <p class=\"fb-field__error\">Obbligatorio con questo trigger (START_EVENT_MISSING).</p>\r\n }\r\n </div>\r\n <div class=\"fb-field\">\r\n <label class=\"fb-field__label\">Nome dell\u2019evento</label>\r\n <input\r\n class=\"fb-input fb-input--mono\"\r\n [value]=\"start().eventName || ''\"\r\n (input)=\"setEventField('eventName', $any($event.target).value)\"\r\n />\r\n <p class=\"fb-field__hint\">Facoltativo: serve dove il tipo da solo non identifica l\u2019evento.</p>\r\n </div>\r\n </fieldset>\r\n}\r\n\r\n<fb-connector-editor\r\n [nodeName]=\"'$start'\"\r\n [node]=\"startNode()\"\r\n [outlets]=\"outlets()\"\r\n title=\"Da dove comincia\"\r\n (connectorChanged)=\"onConnectorChanged($event)\"\r\n/>\r\n", dependencies: [{ kind: "component", type: ConnectorEditorComponent, selector: "fb-connector-editor", inputs: ["nodeName", "node", "outlets", "title"], outputs: ["connectorChanged"] }, { kind: "component", type: ObjectPickerComponent, selector: "fb-object-picker", inputs: ["value", "label", "placeholder", "disabled"], outputs: ["valueChange"] }, { kind: "component", type: RecordFilterEditorComponent, selector: "fb-record-filter-editor", inputs: ["holder", "object", "title", "usage", "fieldOptions", "supportsLogic", "supportsFormula", "emptyWarning", "emptyWarningSeverity"], outputs: ["changed"] }, { kind: "directive", type: SelectValueDirective, selector: "select[fbValue]", inputs: ["fbValue"] }], changeDetection: i0.ChangeDetectionStrategy.OnPush });
14449
14871
  }
14450
14872
  i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.31", ngImport: i0, type: StartInspectorComponent, decorators: [{
14451
14873
  type: Component,
@@ -14454,7 +14876,7 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.31", ngImpo
14454
14876
  ObjectPickerComponent,
14455
14877
  RecordFilterEditorComponent,
14456
14878
  SelectValueDirective,
14457
- ], changeDetection: ChangeDetectionStrategy.OnPush, template: "@if (!hasEntryPoint()) {\r\n <p class=\"fb-callout fb-callout--error\">\r\n Lo Start non punta a nessun elemento: il flow non ha un punto di ingresso e non e\u2019 eseguibile\r\n (START_NO_ENTRY_POINT).\r\n </p>\r\n}\r\n\r\n<div class=\"fb-field\">\r\n <label class=\"fb-field__label\">Quando parte</label>\r\n <!--\r\n \u00A73.4 \u2014 i trigger sono quelli del **tipo di flow** scelto alla creazione, non tutti: la coppia\r\n `processType` + `triggerType` **e'** il tipo, e cambiarla qui vorrebbe dire cambiarlo.\r\n -->\r\n <select class=\"fb-select\" [fbValue]=\"triggerType()\" (change)=\"setTriggerType($any($event.target).value)\">\r\n @for (trigger of triggerTypes(); track trigger.value) {\r\n <option [value]=\"trigger.value\">{{ trigger.label }}</option>\r\n }\r\n </select>\r\n @if (flowType(); as type) {\r\n <p class=\"fb-field__hint\">\r\n Tipo di flow: <strong>{{ type.label }}</strong\r\n >{{ type.description ? ' \u2014 ' + type.description : '' }}\r\n </p>\r\n }\r\n @if (requiresRegistration()) {\r\n <!--\r\n \u00A78 \u2014 l'unico caso in cui una definizione **valida** puo' non essere attivabile: il flow non\r\n parte da solo, e l'infrastruttura dell'ospite deve prenderlo in carico. Dirlo qui, dove si\r\n sceglie il trigger, e' dirlo prima che l'attivazione fallisca.\r\n -->\r\n <p class=\"fb-callout\">\r\n Questo flow non parte da solo: attivandolo,\r\n {{ registrationLabel() ? 'il servizio \u00AB' + registrationLabel() + '\u00BB' : 'l\u2019infrastruttura dell\u2019ambiente' }}\r\n deve <strong>prenderlo in carico</strong>. Se non c\u2019e\u2019 chi lo faccia, l\u2019attivazione non riesce\r\n anche con la definizione a posto.\r\n </p>\r\n }\r\n</div>\r\n\r\n@if (isRecordTrigger()) {\r\n <p class=\"fb-callout\">\r\n Con un trigger su record il flow espone <code>$Record</code> e <code>$Record__Prior</code>.\r\n </p>\r\n\r\n <div class=\"fb-field\">\r\n <label class=\"fb-field__label fb-field__label--required\">Oggetto che innesca</label>\r\n <fb-object-picker\r\n [value]=\"start().object\"\r\n label=\"Oggetto che innesca\"\r\n placeholder=\"Scrivi o scegli un oggetto\"\r\n (valueChange)=\"setObject($event ?? '')\"\r\n />\r\n @if (!start().object) {\r\n <p class=\"fb-field__error\">Obbligatorio con i trigger su record (START_OBJECT_MISSING).</p>\r\n }\r\n </div>\r\n\r\n <div class=\"fb-field\">\r\n <label class=\"fb-field__label\">Su quale operazione</label>\r\n <select\r\n class=\"fb-select\"\r\n [fbValue]=\"start().recordTriggerType || ''\"\r\n (change)=\"setRecordTriggerType($any($event.target).value)\"\r\n >\r\n <option value=\"\">\u2014 scegli \u2014</option>\r\n @for (type of recordTriggerTypes(); track type.value) {\r\n <option [value]=\"type.value\">{{ type.label }}</option>\r\n }\r\n </select>\r\n </div>\r\n\r\n <fb-record-filter-editor\r\n [holder]=\"$any(start())\"\r\n [object]=\"start().object\"\r\n title=\"Criteri di ingresso\"\r\n usage=\"filterable\"\r\n [supportsLogic]=\"true\"\r\n [supportsFormula]=\"true\"\r\n emptyWarning=\"Senza criteri il flow parte su ogni record.\"\r\n (changed)=\"onFiltersChanged($event)\"\r\n />\r\n\r\n <label class=\"fb-check\">\r\n <input\r\n type=\"checkbox\"\r\n [checked]=\"start().doesRequireRecordChangedToMeetCriteria === true\"\r\n (change)=\"setRequireChanged($any($event.target).checked)\"\r\n />\r\n Solo se il record <em>non</em> soddisfaceva i criteri prima del salvataggio\r\n </label>\r\n}\r\n\r\n@if (isScheduled()) {\r\n <fieldset class=\"fb-section\">\r\n <legend class=\"fb-section__title\">Pianificazione</legend>\r\n <div class=\"fb-field\">\r\n <label class=\"fb-field__label fb-field__label--required\">Frequenza</label>\r\n <select\r\n class=\"fb-select\"\r\n [fbValue]=\"start().schedule?.frequency || ''\"\r\n (change)=\"setFrequency($any($event.target).value)\"\r\n >\r\n <option value=\"\">\u2014 scegli \u2014</option>\r\n @for (frequency of frequencies(); track frequency.value) {\r\n <option [value]=\"frequency.value\">{{ frequency.label }}</option>\r\n }\r\n </select>\r\n </div>\r\n <div class=\"fb-field__row\">\r\n <div class=\"fb-field\">\r\n <label class=\"fb-field__label\">Data di inizio</label>\r\n <input\r\n class=\"fb-input\"\r\n type=\"date\"\r\n [value]=\"startDateValue()\"\r\n (input)=\"setStartDate($any($event.target).value)\"\r\n />\r\n </div>\r\n <div class=\"fb-field\">\r\n <label class=\"fb-field__label\">Ora</label>\r\n <input\r\n class=\"fb-input\"\r\n type=\"time\"\r\n [value]=\"startTimeValue()\"\r\n (input)=\"setStartTime($any($event.target).value)\"\r\n />\r\n </div>\r\n </div>\r\n @if (!start().schedule?.frequency) {\r\n <p class=\"fb-field__error\">La pianificazione e\u2019 obbligatoria con questo trigger (START_SCHEDULE_MISSING).</p>\r\n }\r\n </fieldset>\r\n\r\n <!--\r\n \u00A73.4 \u2014 su uno schedulato l'oggetto e' **facoltativo** e vuol dire un'altra cosa che sui trigger\r\n su record: non \u00ABl'entita' che innesca\u00BB ma \u00ABun'esecuzione per ogni record che soddisfa i filtri\u00BB.\r\n Senza, il flow gira una volta sola.\r\n -->\r\n <fieldset class=\"fb-section\">\r\n <legend class=\"fb-section__title\">Un\u2019esecuzione per record</legend>\r\n <p class=\"fb-section__note\">\r\n Facoltativo. Indicando un\u2019entita\u2019, il flow gira <strong>una volta per ogni record</strong> che\r\n soddisfa i filtri invece di una volta sola.\r\n </p>\r\n <div class=\"fb-field\">\r\n <label class=\"fb-field__label\">Entita\u2019</label>\r\n <fb-object-picker\r\n [value]=\"start().object\"\r\n label=\"Entita\u2019\"\r\n placeholder=\"Nessuna: una sola esecuzione\"\r\n (valueChange)=\"setObject($event ?? '')\"\r\n />\r\n </div>\r\n @if (start().object) {\r\n <fb-record-filter-editor\r\n [holder]=\"$any(start())\"\r\n [object]=\"start().object\"\r\n title=\"Quali record\"\r\n usage=\"filterable\"\r\n [supportsLogic]=\"true\"\r\n [supportsFormula]=\"true\"\r\n emptyWarning=\"Senza criteri il flow gira su ogni record dell\u2019entita\u2019.\"\r\n (changed)=\"onFiltersChanged($event)\"\r\n />\r\n }\r\n </fieldset>\r\n\r\n <fieldset class=\"fb-section\">\r\n <legend class=\"fb-section__title\">Percorsi differiti</legend>\r\n <p class=\"fb-section__note\">\r\n Ogni percorso e\u2019 un ramo che parte dopo un intervallo, calcolato da un istante di riferimento.\r\n </p>\r\n\r\n <div class=\"fb-list\">\r\n @for (path of scheduledPaths(); track $index) {\r\n <div class=\"fb-list__item\">\r\n <div class=\"fb-list__header\">\r\n <span class=\"fb-list__index\">{{ $index + 1 }}</span>\r\n <span class=\"fb-list__spacer\"></span>\r\n <button\r\n type=\"button\"\r\n class=\"fb-btn fb-btn--ghost fb-btn--icon\"\r\n aria-label=\"Rimuovi il percorso\"\r\n (click)=\"removeScheduledPath($index)\"\r\n >\r\n \u00D7\r\n </button>\r\n </div>\r\n\r\n <div class=\"fb-field\">\r\n <label class=\"fb-field__label\">Etichetta</label>\r\n <input\r\n class=\"fb-input\"\r\n [value]=\"path.label || ''\"\r\n (input)=\"setPathField($index, 'label', $any($event.target).value)\"\r\n />\r\n </div>\r\n <div class=\"fb-field\">\r\n <label class=\"fb-field__label fb-field__label--required\">Nome tecnico</label>\r\n <input\r\n class=\"fb-input fb-input--mono\"\r\n [value]=\"path.name || ''\"\r\n (input)=\"setPathField($index, 'name', $any($event.target).value)\"\r\n />\r\n </div>\r\n <div class=\"fb-field__row\">\r\n <div class=\"fb-field\">\r\n <label class=\"fb-field__label\">Dopo</label>\r\n <input\r\n class=\"fb-input\"\r\n type=\"number\"\r\n [value]=\"path.offsetNumber ?? ''\"\r\n (input)=\"setPathOffsetNumber($index, $any($event.target).value)\"\r\n />\r\n </div>\r\n <div class=\"fb-field\">\r\n <label class=\"fb-field__label\">Unita\u2019</label>\r\n <select\r\n class=\"fb-select\"\r\n [fbValue]=\"path.offsetUnit || ''\"\r\n (change)=\"setPathField($index, 'offsetUnit', $any($event.target).value)\"\r\n >\r\n <option value=\"\">\u2014</option>\r\n @for (unit of offsetUnits(); track unit.value) {\r\n <option [value]=\"unit.value\">{{ unit.label }}</option>\r\n }\r\n </select>\r\n </div>\r\n </div>\r\n <div class=\"fb-field\">\r\n <label class=\"fb-field__label\">Istante di riferimento</label>\r\n <input\r\n class=\"fb-input\"\r\n [value]=\"path.timeSource || ''\"\r\n placeholder=\"Es. RecordTriggerEvent\"\r\n (input)=\"setPathField($index, 'timeSource', $any($event.target).value)\"\r\n />\r\n </div>\r\n <div class=\"fb-field\">\r\n <label class=\"fb-field__label\">Campo data del record</label>\r\n <input\r\n class=\"fb-input\"\r\n [value]=\"path.recordField || ''\"\r\n (input)=\"setPathField($index, 'recordField', $any($event.target).value)\"\r\n />\r\n </div>\r\n </div>\r\n } @empty {\r\n <p class=\"fb-empty\">Nessun percorso differito.</p>\r\n }\r\n </div>\r\n <button type=\"button\" class=\"fb-btn\" (click)=\"addScheduledPath()\">Aggiungi percorso</button>\r\n </fieldset>\r\n}\r\n\r\n@if (triggerType() === 'None') {\r\n <p class=\"fb-field__hint\">\r\n Il flow parte solo su richiesta. I valori iniziali si passano nelle variabili di input.\r\n </p>\r\n}\r\n\r\n@if (isEvent()) {\r\n <fieldset class=\"fb-section\">\r\n <legend class=\"fb-section__title\">Evento</legend>\r\n <p class=\"fb-section__note\">\r\n Il flow parte alla ricezione di un evento: e\u2019 a questo che l\u2019host si sottoscrive quando la\r\n versione viene attivata (\u00A78).\r\n </p>\r\n <div class=\"fb-field\">\r\n <label class=\"fb-field__label fb-field__label--required\">Tipo di evento</label>\r\n <input\r\n class=\"fb-input fb-input--mono\"\r\n [value]=\"start().eventType || ''\"\r\n placeholder=\"Es. OrdineEvaso\"\r\n (input)=\"setEventField('eventType', $any($event.target).value)\"\r\n />\r\n @if (!start().eventType) {\r\n <p class=\"fb-field__error\">Obbligatorio con questo trigger (START_EVENT_MISSING).</p>\r\n }\r\n </div>\r\n <div class=\"fb-field\">\r\n <label class=\"fb-field__label\">Nome dell\u2019evento</label>\r\n <input\r\n class=\"fb-input fb-input--mono\"\r\n [value]=\"start().eventName || ''\"\r\n (input)=\"setEventField('eventName', $any($event.target).value)\"\r\n />\r\n <p class=\"fb-field__hint\">Facoltativo: serve dove il tipo da solo non identifica l\u2019evento.</p>\r\n </div>\r\n </fieldset>\r\n}\r\n\r\n<fb-connector-editor\r\n [nodeName]=\"'$start'\"\r\n [node]=\"startNode()\"\r\n [outlets]=\"outlets()\"\r\n title=\"Da dove comincia\"\r\n (connectorChanged)=\"onConnectorChanged($event)\"\r\n/>\r\n" }]
14879
+ ], changeDetection: ChangeDetectionStrategy.OnPush, template: "@if (!hasEntryPoint()) {\r\n <p class=\"fb-callout fb-callout--error\">\r\n Lo Start non punta a nessun elemento: il flow non ha un punto di ingresso e non e\u2019 eseguibile\r\n (START_NO_ENTRY_POINT).\r\n </p>\r\n}\r\n\r\n<!--\r\n \u00A73.4 \u2014 su un flow innescato da un record la tendina non c'e': i suoi tre valori sono il *momento*,\r\n e il momento e' l'ultima domanda della sequenza qui sotto, posta fra i soli momenti che\r\n l'operazione scelta ammette. Altrove i trigger sono quelli del **tipo di flow** scelto alla\r\n creazione, non tutti: la coppia `processType` + `triggerType` **e'** il tipo, e cambiarla qui\r\n vorrebbe dire cambiarlo.\r\n-->\r\n@if (!isRecordFlowType()) {\r\n <div class=\"fb-field\">\r\n <label class=\"fb-field__label\">Quando parte</label>\r\n <select class=\"fb-select\" [fbValue]=\"triggerType()\" (change)=\"setTriggerType($any($event.target).value)\">\r\n @for (trigger of triggerTypes(); track trigger.value) {\r\n <option [value]=\"trigger.value\">{{ trigger.label }}</option>\r\n }\r\n </select>\r\n </div>\r\n}\r\n\r\n@if (flowType(); as type) {\r\n <p class=\"fb-field__hint\">\r\n Tipo di flow: <strong>{{ type.label }}</strong\r\n >{{ type.description ? ' \u2014 ' + type.description : '' }}\r\n </p>\r\n}\r\n@if (requiresRegistration()) {\r\n <!--\r\n \u00A78 \u2014 l'unico caso in cui una definizione **valida** puo' non essere attivabile: il flow non\r\n parte da solo, e l'infrastruttura dell'ospite deve prenderlo in carico. Dirlo qui, dove si\r\n sceglie il trigger, e' dirlo prima che l'attivazione fallisca.\r\n -->\r\n <p class=\"fb-callout\">\r\n Questo flow non parte da solo: attivandolo,\r\n {{ registrationLabel() ? 'il servizio \u00AB' + registrationLabel() + '\u00BB' : 'l\u2019infrastruttura dell\u2019ambiente' }}\r\n deve <strong>prenderlo in carico</strong>. Se non c\u2019e\u2019 chi lo faccia, l\u2019attivazione non riesce\r\n anche con la definizione a posto.\r\n </p>\r\n}\r\n\r\n@if (isRecordTrigger()) {\r\n <p class=\"fb-callout\">\r\n Con un trigger su record il flow espone <code>$Record</code> e <code>$Record__Prior</code>.\r\n </p>\r\n\r\n <div class=\"fb-field\">\r\n <label class=\"fb-field__label fb-field__label--required\">Oggetto che innesca</label>\r\n <fb-object-picker\r\n [value]=\"start().object\"\r\n label=\"Oggetto che innesca\"\r\n placeholder=\"Scrivi o scegli un oggetto\"\r\n (valueChange)=\"setObject($event ?? '')\"\r\n />\r\n @if (!start().object) {\r\n <p class=\"fb-field__error\">Obbligatorio con i trigger su record (START_OBJECT_MISSING).</p>\r\n }\r\n </div>\r\n\r\n <!--\r\n \u00A73.4 \u2014 **l'operazione prima, il momento dopo**: sono due campi ma non due assi liberi, e da due\r\n tendine indipendenti si dichiara \u00ABprima dell'eliminazione\u00BB insieme a \u00ABalla creazione\u00BB, che il\r\n runtime legge come un'eliminazione. Qui quella coppia non e' esprimibile.\r\n -->\r\n <div class=\"fb-field\">\r\n <label class=\"fb-field__label fb-field__label--required\">Su quale operazione</label>\r\n <select\r\n class=\"fb-select\"\r\n [fbValue]=\"start().recordTriggerType || ''\"\r\n (change)=\"setRecordOperation($any($event.target).value)\"\r\n >\r\n <option value=\"\">\u2014 scegli \u2014</option>\r\n @for (type of recordTriggerTypes(); track type.value) {\r\n <option [value]=\"type.value\" [title]=\"type.description || type.label\">{{ type.label }}</option>\r\n }\r\n </select>\r\n @if (recordOperation()?.description; as description) {\r\n <p class=\"fb-field__hint\">{{ description }}</p>\r\n }\r\n @if (!start().recordTriggerType) {\r\n <!-- Avviso e non errore: il motore lo legge come \u00ABogni salvataggio\u00BB (\u00A73.4). -->\r\n <p class=\"fb-field__hint fb-field__hint--warn\">\r\n Senza operazione il flow parte su <strong>ogni</strong> salvataggio, creazioni e\r\n aggiornamenti (START_RECORD_TRIGGER_MISSING).\r\n </p>\r\n }\r\n </div>\r\n\r\n <fb-record-filter-editor\r\n [holder]=\"$any(start())\"\r\n [object]=\"start().object\"\r\n title=\"Criteri di ingresso\"\r\n usage=\"filterable\"\r\n [supportsLogic]=\"true\"\r\n [supportsFormula]=\"true\"\r\n emptyWarning=\"Senza criteri il flow parte su ogni record.\"\r\n (changed)=\"onFiltersChanged($event)\"\r\n />\r\n\r\n @if (showRequireChanged()) {\r\n <!--\r\n \u00A73.4 \u2014 il \u00ABprima\u00BB da confrontare c'e' solo dove l'operazione lo porta (`usesPriorRecord`):\r\n alla creazione e all'eliminazione questo flag \u2014 e con lui l'operatore `IsChanged` \u2014 non decide\r\n nulla, quindi la domanda non si pone.\r\n -->\r\n <label class=\"fb-check\">\r\n <input\r\n type=\"checkbox\"\r\n [checked]=\"start().doesRequireRecordChangedToMeetCriteria === true\"\r\n (change)=\"setRequireChanged($any($event.target).checked)\"\r\n />\r\n Solo se il record <em>non</em> soddisfaceva i criteri prima del salvataggio\r\n </label>\r\n } @else if (start().doesRequireRecordChangedToMeetCriteria === true) {\r\n <!--\r\n Il flag c'e' ma l'operazione non ha un \u00ABprima\u00BB: arriva da un documento scritto altrove, o da\r\n un'operazione cambiata prima di questa versione dell'editor. Nasconderlo e basta lo\r\n toglierebbe dalla vista lasciandolo scritto \u2014 si dice, e la ripulitura e' un comando.\r\n E' un **avviso** e non un errore, a differenza di una condizione che legge i valori\r\n precedenti: questo il runtime lo ignora invece di fallire (\u00A77).\r\n -->\r\n <p class=\"fb-callout fb-callout--warn\">\r\n Il documento chiede che il record <strong>non</strong> soddisfacesse i criteri prima del\r\n salvataggio, ma con questa operazione non c\u2019e\u2019 un \u00ABprima\u00BB da confrontare: il motore lo ignora\r\n (PRIOR_VALUES_NOT_AVAILABLE).\r\n <button type=\"button\" class=\"fb-btn fb-btn--ghost\" (click)=\"setRequireChanged(false)\">\r\n Togli la condizione\r\n </button>\r\n </p>\r\n }\r\n\r\n <!--\r\n Il momento e' l'**ultima** domanda, e sta qui solo dove la tendina in cima non c'e': nel ripiego\r\n il `triggerType` lo governa quella, e due controlli sullo stesso campo si contraddicono a vista.\r\n -->\r\n @if (isRecordFlowType()) {\r\n <div class=\"fb-field\">\r\n <label class=\"fb-field__label fb-field__label--required\">In che momento</label>\r\n @if (showTimingChoice()) {\r\n <select class=\"fb-select\" [fbValue]=\"triggerType()\" (change)=\"setTriggerType($any($event.target).value)\">\r\n @for (timing of timingOptions(); track timing.value) {\r\n <option [value]=\"timing.value\" [title]=\"timing.description || timing.label\">{{ timing.label }}</option>\r\n }\r\n </select>\r\n @if (currentTiming()?.description; as description) {\r\n <p class=\"fb-field__hint\">{{ description }}</p>\r\n }\r\n } @else {\r\n <!--\r\n Momento unico (`allowsTimingChoice: false`, l'eliminazione): la domanda non si pone, ma il\r\n campo si scrive lo stesso \u2014 il contratto chiede **entrambi**.\r\n -->\r\n <p class=\"fb-field__hint\">\r\n <strong>{{ currentTiming()?.label || triggerType() }}</strong> \u2014 con questa operazione e\u2019\r\n l\u2019unico momento possibile, e viene scritto comunque nel documento.\r\n </p>\r\n }\r\n </div>\r\n }\r\n\r\n @if (hasTriggerMismatch()) {\r\n <p class=\"fb-callout fb-callout--error\">\r\n L\u2019operazione e il momento scritti nel documento non stanno insieme (START_TRIGGER_MISMATCH):\r\n il flow parte su un\u2019operazione diversa da quella scelta, e l\u2019attivazione non riesce.\r\n <button type=\"button\" class=\"fb-btn fb-btn--ghost\" (click)=\"useDefaultTiming()\">\r\n Riporta il momento fra quelli ammessi\r\n </button>\r\n </p>\r\n }\r\n}\r\n\r\n@if (isScheduled()) {\r\n <fieldset class=\"fb-section\">\r\n <legend class=\"fb-section__title\">Pianificazione</legend>\r\n <div class=\"fb-field\">\r\n <label class=\"fb-field__label fb-field__label--required\">Frequenza</label>\r\n <select\r\n class=\"fb-select\"\r\n [fbValue]=\"start().schedule?.frequency || ''\"\r\n (change)=\"setFrequency($any($event.target).value)\"\r\n >\r\n <option value=\"\">\u2014 scegli \u2014</option>\r\n @for (frequency of frequencies(); track frequency.value) {\r\n <option [value]=\"frequency.value\">{{ frequency.label }}</option>\r\n }\r\n </select>\r\n </div>\r\n <div class=\"fb-field__row\">\r\n <div class=\"fb-field\">\r\n <label class=\"fb-field__label\">Data di inizio</label>\r\n <input\r\n class=\"fb-input\"\r\n type=\"date\"\r\n [value]=\"startDateValue()\"\r\n (input)=\"setStartDate($any($event.target).value)\"\r\n />\r\n </div>\r\n <div class=\"fb-field\">\r\n <label class=\"fb-field__label\">Ora</label>\r\n <input\r\n class=\"fb-input\"\r\n type=\"time\"\r\n [value]=\"startTimeValue()\"\r\n (input)=\"setStartTime($any($event.target).value)\"\r\n />\r\n </div>\r\n </div>\r\n @if (!start().schedule?.frequency) {\r\n <p class=\"fb-field__error\">La pianificazione e\u2019 obbligatoria con questo trigger (START_SCHEDULE_MISSING).</p>\r\n }\r\n </fieldset>\r\n\r\n <!--\r\n \u00A73.4 \u2014 su uno schedulato l'oggetto e' **facoltativo** e vuol dire un'altra cosa che sui trigger\r\n su record: non \u00ABl'entita' che innesca\u00BB ma \u00ABun'esecuzione per ogni record che soddisfa i filtri\u00BB.\r\n Senza, il flow gira una volta sola.\r\n -->\r\n <fieldset class=\"fb-section\">\r\n <legend class=\"fb-section__title\">Un\u2019esecuzione per record</legend>\r\n <p class=\"fb-section__note\">\r\n Facoltativo. Indicando un\u2019entita\u2019, il flow gira <strong>una volta per ogni record</strong> che\r\n soddisfa i filtri invece di una volta sola.\r\n </p>\r\n <div class=\"fb-field\">\r\n <label class=\"fb-field__label\">Entita\u2019</label>\r\n <fb-object-picker\r\n [value]=\"start().object\"\r\n label=\"Entita\u2019\"\r\n placeholder=\"Nessuna: una sola esecuzione\"\r\n (valueChange)=\"setObject($event ?? '')\"\r\n />\r\n </div>\r\n @if (start().object) {\r\n <fb-record-filter-editor\r\n [holder]=\"$any(start())\"\r\n [object]=\"start().object\"\r\n title=\"Quali record\"\r\n usage=\"filterable\"\r\n [supportsLogic]=\"true\"\r\n [supportsFormula]=\"true\"\r\n emptyWarning=\"Senza criteri il flow gira su ogni record dell\u2019entita\u2019.\"\r\n (changed)=\"onFiltersChanged($event)\"\r\n />\r\n }\r\n </fieldset>\r\n\r\n <fieldset class=\"fb-section\">\r\n <legend class=\"fb-section__title\">Percorsi differiti</legend>\r\n <p class=\"fb-section__note\">\r\n Ogni percorso e\u2019 un ramo che parte dopo un intervallo, calcolato da un istante di riferimento.\r\n </p>\r\n\r\n <div class=\"fb-list\">\r\n @for (path of scheduledPaths(); track $index) {\r\n <div class=\"fb-list__item\">\r\n <div class=\"fb-list__header\">\r\n <span class=\"fb-list__index\">{{ $index + 1 }}</span>\r\n <span class=\"fb-list__spacer\"></span>\r\n <button\r\n type=\"button\"\r\n class=\"fb-btn fb-btn--ghost fb-btn--icon\"\r\n aria-label=\"Rimuovi il percorso\"\r\n (click)=\"removeScheduledPath($index)\"\r\n >\r\n \u00D7\r\n </button>\r\n </div>\r\n\r\n <div class=\"fb-field\">\r\n <label class=\"fb-field__label\">Etichetta</label>\r\n <input\r\n class=\"fb-input\"\r\n [value]=\"path.label || ''\"\r\n (input)=\"setPathField($index, 'label', $any($event.target).value)\"\r\n />\r\n </div>\r\n <div class=\"fb-field\">\r\n <label class=\"fb-field__label fb-field__label--required\">Nome tecnico</label>\r\n <input\r\n class=\"fb-input fb-input--mono\"\r\n [value]=\"path.name || ''\"\r\n (input)=\"setPathField($index, 'name', $any($event.target).value)\"\r\n />\r\n </div>\r\n <div class=\"fb-field__row\">\r\n <div class=\"fb-field\">\r\n <label class=\"fb-field__label\">Dopo</label>\r\n <input\r\n class=\"fb-input\"\r\n type=\"number\"\r\n [value]=\"path.offsetNumber ?? ''\"\r\n (input)=\"setPathOffsetNumber($index, $any($event.target).value)\"\r\n />\r\n </div>\r\n <div class=\"fb-field\">\r\n <label class=\"fb-field__label\">Unita\u2019</label>\r\n <select\r\n class=\"fb-select\"\r\n [fbValue]=\"path.offsetUnit || ''\"\r\n (change)=\"setPathField($index, 'offsetUnit', $any($event.target).value)\"\r\n >\r\n <option value=\"\">\u2014</option>\r\n @for (unit of offsetUnits(); track unit.value) {\r\n <option [value]=\"unit.value\">{{ unit.label }}</option>\r\n }\r\n </select>\r\n </div>\r\n </div>\r\n <div class=\"fb-field\">\r\n <label class=\"fb-field__label\">Istante di riferimento</label>\r\n <input\r\n class=\"fb-input\"\r\n [value]=\"path.timeSource || ''\"\r\n placeholder=\"Es. RecordTriggerEvent\"\r\n (input)=\"setPathField($index, 'timeSource', $any($event.target).value)\"\r\n />\r\n </div>\r\n <div class=\"fb-field\">\r\n <label class=\"fb-field__label\">Campo data del record</label>\r\n <input\r\n class=\"fb-input\"\r\n [value]=\"path.recordField || ''\"\r\n (input)=\"setPathField($index, 'recordField', $any($event.target).value)\"\r\n />\r\n </div>\r\n </div>\r\n } @empty {\r\n <p class=\"fb-empty\">Nessun percorso differito.</p>\r\n }\r\n </div>\r\n <button type=\"button\" class=\"fb-btn\" (click)=\"addScheduledPath()\">Aggiungi percorso</button>\r\n </fieldset>\r\n}\r\n\r\n@if (triggerType() === 'None') {\r\n <p class=\"fb-field__hint\">\r\n Il flow parte solo su richiesta. I valori iniziali si passano nelle variabili di input.\r\n </p>\r\n}\r\n\r\n@if (isEvent()) {\r\n <fieldset class=\"fb-section\">\r\n <legend class=\"fb-section__title\">Evento</legend>\r\n <p class=\"fb-section__note\">\r\n Il flow parte alla ricezione di un evento: e\u2019 a questo che l\u2019host si sottoscrive quando la\r\n versione viene attivata (\u00A78).\r\n </p>\r\n <div class=\"fb-field\">\r\n <label class=\"fb-field__label fb-field__label--required\">Tipo di evento</label>\r\n <input\r\n class=\"fb-input fb-input--mono\"\r\n [value]=\"start().eventType || ''\"\r\n placeholder=\"Es. OrdineEvaso\"\r\n (input)=\"setEventField('eventType', $any($event.target).value)\"\r\n />\r\n @if (!start().eventType) {\r\n <p class=\"fb-field__error\">Obbligatorio con questo trigger (START_EVENT_MISSING).</p>\r\n }\r\n </div>\r\n <div class=\"fb-field\">\r\n <label class=\"fb-field__label\">Nome dell\u2019evento</label>\r\n <input\r\n class=\"fb-input fb-input--mono\"\r\n [value]=\"start().eventName || ''\"\r\n (input)=\"setEventField('eventName', $any($event.target).value)\"\r\n />\r\n <p class=\"fb-field__hint\">Facoltativo: serve dove il tipo da solo non identifica l\u2019evento.</p>\r\n </div>\r\n </fieldset>\r\n}\r\n\r\n<fb-connector-editor\r\n [nodeName]=\"'$start'\"\r\n [node]=\"startNode()\"\r\n [outlets]=\"outlets()\"\r\n title=\"Da dove comincia\"\r\n (connectorChanged)=\"onConnectorChanged($event)\"\r\n/>\r\n" }]
14458
14880
  }] });
14459
14881
 
14460
14882
  /**
@@ -19529,5 +19951,5 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.31", ngImpo
19529
19951
  * Generated bundle index. Do not edit.
19530
19952
  */
19531
19953
 
19532
- export { ConditionEditorComponent, ConnectorEditorComponent, DebugPanelComponent, DynamicScreenInspectorComponent, ENUM_CHOICE_SET_DEFAULT_DISPLAY_FIELD, ENUM_CHOICE_SET_DEFAULT_VALUE_FIELD, ENUM_CHOICE_SET_FIELDS, ElementDialogComponent, ElementInspectorComponent, ElementPaletteComponent, EnumValuePickerComponent, FALLBACK_COLLECTION_BY_TYPE, FALLBACK_TYPE_LABEL, FLOW_BUILDER_HTTP_CONFIG, FLOW_CLIPBOARD_KIND, FLOW_CLIPBOARD_VERSION, FLOW_ELEMENT_ICONS, FLOW_ELEMENT_VARIANT_FIELDS, FLOW_ELEMENT_VARIANT_ICONS, FLOW_ERROR_FALLBACK_MESSAGE, FLOW_ERROR_HTTP_STATUS, FLOW_FOLD_HEIGHT, FLOW_FOLD_WIDTH, FLOW_GROUP_COLORS, FLOW_NAME_PATTERN, FLOW_NODE_COLLECTIONS, FLOW_NODE_HEIGHT, FLOW_NODE_WIDTH, FLOW_REFERENCE_FIELDS, FLOW_RESOURCE_COLLECTIONS, FLOW_VALUE_FIELDS, FLOW_VALUE_LITERAL_FIELDS, FieldAssignmentEditorComponent, FieldPickerComponent, FieldValuePickerComponent, FlowApiError, FlowBuilderApi, FlowBuilderComponent, FlowCanvasComponent, FlowCatalogStore, FlowClipboardService, FlowDictionaryStore, FlowDocumentStore, FlowEditorSession, FlowLayoutService, FlowValidationStore, FormulaEditorComponent, FormulaValidationService, GROUP_HEADER_HEIGHT, GROUP_MIN_HEIGHT, GROUP_MIN_WIDTH, GROUP_PADDING, GroupPanelComponent, HttpFlowBuilderApi, MergeTextEditorComponent, NamePickerComponent, NodeInspectorBase, ORCHESTRATION_CONDITION_OUTPUT, ObjectPickerComponent, OrchestratedStageInspectorComponent, ParameterEditorComponent, PasteDialogComponent, ProblemsPanelComponent, RecordFilterEditorComponent, ReferencePickerComponent, ResourceDialogComponent, ResourceFormComponent, ResourcePanelComponent, RunDialogComponent, SEVERITY_BUCKETS, SEVERITY_ICON, SEVERITY_LABEL, START_NODE_NAME, SelectValueDirective, StartInspectorComponent, StructureMemberPickerComponent, StructurePickerComponent, TYPES_WITH_AUTOMATIC_OUTPUT, TYPE_BY_COLLECTION, UNSUPPORTED_TYPES, ValueEditorComponent, VersionPanelComponent, allFieldsOf, applyPaste, areTypesComparable, boundsAround, buildClipboardPayload, canvasFoldId, canvasGroupId, canvasNodeId, checkConditionLogic, checkFlowName, choiceSetSourceOf, declaredChoiceSetSources, describeActivationError, describeFieldPath, describePathEntry, duplicateField, elementIcon, emptyFlowDefinition, enumChoiceSetFieldType, fieldAt, filterReferences, flattenFields, flowNodeWidth, flowNodeWidthClass, foldSourceConnectorId, foldTargetConnectorId, groupColorClass, groupRect, hasAutoSize, innerNamesOf, insertField, isClipboardPayload, isCustomConditionLogic, isEmptyReferenceFilter, isFieldResource, isFoldReference, isGlobalReference, isNumericType, isPathInside, isRegistrationError, isTypeCheckedOperator, isUnknownEnumChoiceSetField, isValidFlowName, isValued, isVersionActive, loadPathLevel, matchesReferenceFilter, membersInside, moveCondition, moveField, navigatePath, otherSourceFieldsOf, outletByKey, outletsOf, parseCanvasFoldId, parseCanvasGroupId, parseCanvasNodeId, parseFieldPath, parseInvariantNumber, parseSourceConnectorId, parseTargetConnectorId, pathAvailableNames, pathContainerLabel, pathKey, pathNotVerifiableMessage, planPaste, recomputeMembers, referenceRoot, referencedRootsOf, remapConditionLogic, removeCondition, removeField, repeatedFieldNames, repeatingSectionOf, resolvePath, resourceFieldsOf, rewriteReferences, sameMembers, samePath, screenActionNames, screenFieldNames, severityBucket, slugifyFlowName, sourceConnectorId, stageStepNames, stageStepOutputReferenced, stepsOf, targetConnectorId, typeOfCollection, uniqueFlowName, valuedFieldOf, valuedFieldsOf, variantFieldOf, variantOf, variantPresetOf };
19954
+ export { ConditionEditorComponent, ConnectorEditorComponent, DebugPanelComponent, DynamicScreenInspectorComponent, ENUM_CHOICE_SET_DEFAULT_DISPLAY_FIELD, ENUM_CHOICE_SET_DEFAULT_VALUE_FIELD, ENUM_CHOICE_SET_FIELDS, ElementDialogComponent, ElementInspectorComponent, ElementPaletteComponent, EnumValuePickerComponent, FALLBACK_COLLECTION_BY_TYPE, FALLBACK_TYPE_LABEL, FLOW_BUILDER_HTTP_CONFIG, FLOW_CLIPBOARD_KIND, FLOW_CLIPBOARD_VERSION, FLOW_ELEMENT_ICONS, FLOW_ELEMENT_VARIANT_FIELDS, FLOW_ELEMENT_VARIANT_ICONS, FLOW_ERROR_FALLBACK_MESSAGE, FLOW_ERROR_HTTP_STATUS, FLOW_FOLD_HEIGHT, FLOW_FOLD_WIDTH, FLOW_GROUP_COLORS, FLOW_NAME_PATTERN, FLOW_NODE_COLLECTIONS, FLOW_NODE_HEIGHT, FLOW_NODE_WIDTH, FLOW_REFERENCE_FIELDS, FLOW_RESOURCE_COLLECTIONS, FLOW_VALUE_FIELDS, FLOW_VALUE_LITERAL_FIELDS, FieldAssignmentEditorComponent, FieldPickerComponent, FieldValuePickerComponent, FlowApiError, FlowBuilderApi, FlowBuilderComponent, FlowCanvasComponent, FlowCatalogStore, FlowClipboardService, FlowDictionaryStore, FlowDocumentStore, FlowEditorSession, FlowLayoutService, FlowValidationStore, FormulaEditorComponent, FormulaValidationService, GROUP_HEADER_HEIGHT, GROUP_MIN_HEIGHT, GROUP_MIN_WIDTH, GROUP_PADDING, GroupPanelComponent, HttpFlowBuilderApi, MergeTextEditorComponent, NamePickerComponent, NodeInspectorBase, ORCHESTRATION_CONDITION_OUTPUT, ObjectPickerComponent, OrchestratedStageInspectorComponent, PRIOR_VALUES_OPERATOR, PRIOR_VALUES_SCOPE, ParameterEditorComponent, PasteDialogComponent, ProblemsPanelComponent, RECORD_TRIGGERS, RecordFilterEditorComponent, ReferencePickerComponent, ResourceDialogComponent, ResourceFormComponent, ResourcePanelComponent, RunDialogComponent, SEVERITY_BUCKETS, SEVERITY_ICON, SEVERITY_LABEL, START_NODE_NAME, SelectValueDirective, StartInspectorComponent, StructureMemberPickerComponent, StructurePickerComponent, TYPES_WITH_AUTOMATIC_OUTPUT, TYPE_BY_COLLECTION, UNSUPPORTED_TYPES, ValueEditorComponent, VersionPanelComponent, allFieldsOf, applyPaste, areTypesComparable, boundsAround, buildClipboardPayload, canvasFoldId, canvasGroupId, canvasNodeId, checkConditionLogic, checkFlowName, choiceSetSourceOf, declaredChoiceSetSources, describeActivationError, describeFieldPath, describePathEntry, describePriorValues, duplicateField, elementIcon, emptyFlowDefinition, enumChoiceSetFieldType, fieldAt, filterReferences, flattenFields, flowNodeWidth, flowNodeWidthClass, foldSourceConnectorId, foldTargetConnectorId, groupColorClass, groupRect, hasAutoSize, innerNamesOf, insertField, isClipboardPayload, isCustomConditionLogic, isEmptyReferenceFilter, isFieldResource, isFoldReference, isGlobalReference, isNumericType, isPathInside, isRecordTrigger, isRegistrationError, isTypeCheckedOperator, isUnknownEnumChoiceSetField, isValidFlowName, isValued, isVersionActive, loadPathLevel, matchesReferenceFilter, membersInside, moveCondition, moveField, navigatePath, otherSourceFieldsOf, outletByKey, outletsOf, parseCanvasFoldId, parseCanvasGroupId, parseCanvasNodeId, parseFieldPath, parseInvariantNumber, parseSourceConnectorId, parseTargetConnectorId, pathAvailableNames, pathContainerLabel, pathKey, pathNotVerifiableMessage, planPaste, priorCriteriaSeverity, priorValuesAvailability, priorValuesOfDefinition, priorValuesProposable, priorValuesSeverity, readsPriorValues, recomputeMembers, referenceRoot, referencedRootsOf, remapConditionLogic, removeCondition, removeField, repeatedFieldNames, repeatingSectionOf, resolvePath, resourceFieldsOf, rewriteReferences, sameMembers, samePath, screenActionNames, screenFieldNames, severityBucket, slugifyFlowName, sourceConnectorId, stageStepNames, stageStepOutputReferenced, stepsOf, targetConnectorId, typeOfCollection, uniqueFlowName, valuedFieldOf, valuedFieldsOf, variantFieldOf, variantOf, variantPresetOf };
19533
19955
  //# sourceMappingURL=esfaenza-flow-builder.mjs.map