@esfaenza/flow-builder 20.0.0 → 20.3.1

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.
@@ -37,9 +37,9 @@ const FLOW_NODE_COLLECTIONS = [
37
37
  'transforms',
38
38
  'waits',
39
39
  'customErrors',
40
+ 'orchestratedStages',
40
41
  'steps',
41
42
  'experiments',
42
- 'orchestratedStages',
43
43
  ];
44
44
  /** Le collection di risorse (§4.6). Condividono lo spazio dei nomi con i node (§3.3). */
45
45
  const FLOW_RESOURCE_COLLECTIONS = [
@@ -217,7 +217,19 @@ class FlowBuilderApi {
217
217
  void request;
218
218
  return missing('resumeInterview');
219
219
  }
220
- /** `GET /interviews/{key}` — ispeziona una sospesa senza consumarla. */
220
+ /**
221
+ * `POST /interviews/{key}/stage-step` — un assegnatario conclude uno step (§5.13).
222
+ * Se lo stage si sospende di nuovo, la risposta porta una `interviewKey` **nuova**: la
223
+ * vecchia non e' piu' valida e va sostituita, non conservata.
224
+ */
225
+ completeStageStep(request) {
226
+ void request;
227
+ return missing('completeStageStep');
228
+ }
229
+ /**
230
+ * `GET /interviews/{key}` — ispeziona una sospesa senza consumarla.
231
+ * Con `isWaitingForStageStep` porta `stageSteps` con lo stato di ognuno (§5.13).
232
+ */
221
233
  inspectInterview(interviewKey) {
222
234
  void interviewKey;
223
235
  return missing('inspectInterview');
@@ -316,128 +328,134 @@ class HttpFlowBuilderApi extends FlowBuilderApi {
316
328
  }
317
329
  // ---------------------------------------------------------------- §6.1
318
330
  listFlows(query) {
319
- return this.get('/flows', HttpFlowBuilderApi.params({ ...query }));
331
+ return this.get('/flows/editor/list', HttpFlowBuilderApi.params({ ...query }));
320
332
  }
321
333
  listVersions(flowName) {
322
- return this.get(`/flows/${HttpFlowBuilderApi.segment(flowName)}/versions`);
334
+ return this.get(`/flows/editor/${HttpFlowBuilderApi.segment(flowName)}/versions`);
323
335
  }
324
336
  loadFlow(flowName, version) {
325
- return this.get(`/flows/${HttpFlowBuilderApi.segment(flowName)}`, HttpFlowBuilderApi.params({ version }));
337
+ return this.get(`/flows/editor/${HttpFlowBuilderApi.segment(flowName)}`, HttpFlowBuilderApi.params({ version }));
326
338
  }
327
339
  exportFlow(flowName, query) {
328
- return this.request(firstValueFrom(this.http.get(this.url(`/flows/${HttpFlowBuilderApi.segment(flowName)}/export`), {
340
+ return this.request(firstValueFrom(this.http.get(this.url(`/flows/editor/${HttpFlowBuilderApi.segment(flowName)}/export`), {
329
341
  params: HttpFlowBuilderApi.params({ ...query }),
330
342
  responseType: 'text',
331
343
  })));
332
344
  }
333
345
  parseFlow(definition) {
334
- return this.post('/flows/parse', definition);
346
+ return this.post('/flows/editor/parse', definition);
335
347
  }
336
348
  // ---------------------------------------------------------------- §6.2
337
349
  createFlow(request) {
338
- return this.post('/flows', request);
350
+ return this.post('/flows/editor/create', request);
339
351
  }
340
352
  saveFlow(flowName, request) {
341
- return this.put(`/flows/${HttpFlowBuilderApi.segment(flowName)}`, request);
353
+ return this.put(`/flows/editor/save`, request);
342
354
  }
343
355
  createVersion(flowName, request) {
344
- return this.post(`/flows/${HttpFlowBuilderApi.segment(flowName)}/versions`, { author: request?.author }, HttpFlowBuilderApi.params({ from: request?.from }));
356
+ return this.post(`/flows/editor/${HttpFlowBuilderApi.segment(flowName)}/new-version`, { author: request?.author }, HttpFlowBuilderApi.params({ from: request?.from }));
345
357
  }
346
358
  cloneFlow(flowName, request) {
347
- return this.post(`/flows/${HttpFlowBuilderApi.segment(flowName)}/clone`, request);
359
+ return this.post(`/flows/editor/${HttpFlowBuilderApi.segment(flowName)}/clone`, request);
348
360
  }
349
361
  activateVersion(flowName, version) {
350
- return this.post(`/flows/${HttpFlowBuilderApi.segment(flowName)}/versions/${version}/activate`);
362
+ return this.put(`/flows/editor/${HttpFlowBuilderApi.segment(flowName)}/activate/${version}`);
351
363
  }
352
364
  deactivateFlow(flowName) {
353
- return this.post(`/flows/${HttpFlowBuilderApi.segment(flowName)}/deactivate`);
365
+ return this.put(`/flows/editor/${HttpFlowBuilderApi.segment(flowName)}/deactivate`);
354
366
  }
355
367
  deleteVersion(flowName, version) {
356
- return this.del(`/flows/${HttpFlowBuilderApi.segment(flowName)}/versions/${version}`);
368
+ return this.del(`/flows/editor/${HttpFlowBuilderApi.segment(flowName)}/versions/${version}`);
357
369
  }
358
370
  deleteFlow(flowName) {
359
- return this.del(`/flows/${HttpFlowBuilderApi.segment(flowName)}`);
371
+ return this.del(`/flows/editor/${HttpFlowBuilderApi.segment(flowName)}`);
360
372
  }
361
373
  // ---------------------------------------------------------------- §6.3
362
374
  validateDefinition(definition) {
363
- return this.post('/flows/validate', definition);
375
+ return this.post(`/flows/editor/${HttpFlowBuilderApi.segment(definition.fullName.toString())}/validate`, definition);
364
376
  }
365
377
  validateVersion(flowName, version) {
366
- return this.get(`/flows/${HttpFlowBuilderApi.segment(flowName)}/versions/${version}/validate`);
378
+ return this.get(`/flows/editor/${HttpFlowBuilderApi.segment(flowName)}/validate`, HttpFlowBuilderApi.params({ version: version }));
367
379
  }
368
380
  // ---------------------------------------------------------------- §6.4
369
- getDictionaries() {
370
- return this.get('/flows/dictionaries');
381
+ getDictionaries(processType) {
382
+ // `processType` filtra le globali: `$Record` non esiste in uno screen flow (§4.1).
383
+ return this.get('/flows/editor/dictionaries', HttpFlowBuilderApi.params({ processType }));
371
384
  }
372
385
  getReferences(query) {
373
- return this.post('/flows/references', query);
386
+ return this.post('/flows/editor/references', query);
374
387
  }
375
388
  getWritableReferences(query) {
376
- return this.post('/flows/references/writable', query);
389
+ return this.post('/flows/editor/writable-references', query);
377
390
  }
378
391
  getConnectorTargets(definition, excluding) {
379
- return this.post('/flows/connector-targets', definition, HttpFlowBuilderApi.params({ excluding }));
392
+ return this.post('/flows/editor/connector-targets', definition, HttpFlowBuilderApi.params({ excluding }));
380
393
  }
381
394
  getOutline(definition) {
382
- return this.post('/flows/outline', definition);
395
+ return this.post('/flows/editor/outline', definition);
383
396
  }
384
397
  listObjects() {
385
- return this.get('/catalog/objects');
398
+ return this.get('/flows/objects');
386
399
  }
387
400
  describeObject(object) {
388
- return this.get(`/schema/${HttpFlowBuilderApi.segment(object)}`);
401
+ return this.get(`/flows/objects/${HttpFlowBuilderApi.segment(object)}`);
389
402
  }
390
403
  listFields(object, usage) {
391
- return this.get(`/schema/${HttpFlowBuilderApi.segment(object)}/fields`, HttpFlowBuilderApi.params({ usage }));
404
+ return this.get(`/flows/objects/${HttpFlowBuilderApi.segment(object)}/fields`, HttpFlowBuilderApi.params({ usage }));
392
405
  }
393
406
  listFieldValues(object, field) {
394
- return this.get(`/schema/${HttpFlowBuilderApi.segment(object)}/fields/${HttpFlowBuilderApi.segment(field)}/values`);
407
+ return this.get(`/flows/objects/${HttpFlowBuilderApi.segment(object)}/fields/${HttpFlowBuilderApi.segment(field)}/picklist`);
395
408
  }
396
409
  listActionTypes() {
397
- return this.get('/catalog/action-types');
410
+ return this.get('/flows/action-types');
398
411
  }
399
412
  listActions(actionType) {
400
- return this.get('/catalog/actions', HttpFlowBuilderApi.params({ type: actionType }));
413
+ return this.get('/flows/actions', HttpFlowBuilderApi.params({ type: actionType }));
401
414
  }
402
415
  listActionParameters(actionType, actionName) {
403
- return this.get(`/catalog/actions/${HttpFlowBuilderApi.segment(actionType)}/${HttpFlowBuilderApi.segment(actionName)}/parameters`);
416
+ return this.get(`/flows/actions/${HttpFlowBuilderApi.segment(actionType)}/${HttpFlowBuilderApi.segment(actionName)}/parameters`);
404
417
  }
405
418
  listScripts() {
406
- return this.get('/catalog/scripts');
419
+ return this.get('/flows/editor/scripts');
407
420
  }
408
421
  listScriptParameters(scriptName) {
409
- return this.get(`/catalog/scripts/${HttpFlowBuilderApi.segment(scriptName)}/parameters`);
422
+ return this.get(`/flows/editor/scripts/${HttpFlowBuilderApi.segment(scriptName)}/parameters`);
410
423
  }
411
424
  listForms() {
412
- return this.get('/catalog/forms');
425
+ return this.get('/flows/editor/forms');
413
426
  }
414
427
  listFormParameters(formName) {
415
- return this.get(`/catalog/forms/${HttpFlowBuilderApi.segment(formName)}/parameters`);
428
+ return this.get(`/flows/editor/forms/${HttpFlowBuilderApi.segment(formName)}/parameters`);
416
429
  }
417
430
  listEnumTypes() {
418
- return this.get('/catalog/enum-types');
431
+ return this.get('/flows/editor/enum-types');
419
432
  }
420
433
  listEvents() {
421
- return this.get('/catalog/events');
434
+ return this.get('/flows/editor/events');
422
435
  }
423
436
  listSubflowCandidates(excluding) {
424
- return this.get('/flows/subflow-candidates', HttpFlowBuilderApi.params({ excluding }));
437
+ return this.get('/flows/editor/subflows', HttpFlowBuilderApi.params({ excluding }));
425
438
  }
426
439
  // ---------------------------------------------------------------- §6.5
427
440
  startInterview(request) {
428
- return this.post('/interviews', request);
441
+ return this.post('/flows/start', request);
429
442
  }
430
443
  respondToScreen(request) {
431
- return this.post('/interviews/screen', request);
444
+ return this.post('/flows/submit-screen', request);
432
445
  }
433
446
  resumeInterview(request) {
434
- return this.post('/interviews/resume', request);
447
+ return this.post('/flows/resume-wait', request);
448
+ }
449
+ completeStageStep(request) {
450
+ // La chiave sta nell'URL, il resto nel corpo: la risposta puo' portarne una nuova (§5.13).
451
+ let body = { stepName: request.stepName, status: request.status, stepOutputs: request.stepOutputs, debug: request.debug };
452
+ return this.post(`/flows/${HttpFlowBuilderApi.segment(request.interviewKey)}/resume-stage-step`, body);
435
453
  }
436
454
  inspectInterview(interviewKey) {
437
- return this.get(`/interviews/${HttpFlowBuilderApi.segment(interviewKey)}`);
455
+ return this.get(`/flows/${HttpFlowBuilderApi.segment(interviewKey)}`);
438
456
  }
439
457
  abandonInterview(interviewKey) {
440
- return this.del(`/interviews/${HttpFlowBuilderApi.segment(interviewKey)}`);
458
+ return this.del(`/flows/${HttpFlowBuilderApi.segment(interviewKey)}`);
441
459
  }
442
460
  }
443
461
 
@@ -574,6 +592,15 @@ function outletsOf(type, node) {
574
592
  }
575
593
  return outlets;
576
594
  }
595
+ // Lo `faultConnector` di uno stage **non** e' un ramo di guasto: e' il ramo dello step
596
+ // rifiutato, e senza di esso un rifiuto fa fallire l'interview (§5.13). L'etichetta lo
597
+ // dice, altrimenti chi disegna un'approvazione lo lascia vuoto credendo di rinunciare
598
+ // solo alla gestione degli errori.
599
+ case 'OrchestratedStage':
600
+ return [
601
+ field('next', 'Next', 'Stage concluso', 'connector'),
602
+ field('fault', 'Fault', 'Step rifiutato', 'faultConnector'),
603
+ ];
577
604
  case 'ScriptCall':
578
605
  case 'RecordLookup':
579
606
  case 'RecordCreate':
@@ -653,8 +680,8 @@ const FALLBACK_TYPE_LABEL = {
653
680
  Experiment: 'Esperimento',
654
681
  OrchestratedStage: 'Stage di orchestrazione',
655
682
  };
656
- /** I tre tipi che il motore rifiuta: nascosti dalla palette (§3.2, §13.9). */
657
- const UNSUPPORTED_TYPES = new Set(['Step', 'Experiment', 'OrchestratedStage']);
683
+ /** I due tipi che il motore rifiuta: nascosti dalla palette (§3.2, §13.9). */
684
+ const UNSUPPORTED_TYPES = new Set(['Step', 'Experiment']);
658
685
  /** Elementi che espongono il proprio risultato sotto il nome dell'elemento (§4.5). */
659
686
  const TYPES_WITH_AUTOMATIC_OUTPUT = new Set([
660
687
  'RecordLookup',
@@ -691,6 +718,7 @@ const FLOW_ELEMENT_ICONS = {
691
718
  Transform: '⇄',
692
719
  Wait: '◷',
693
720
  CustomError: '!',
721
+ OrchestratedStage: '☰',
694
722
  };
695
723
  /**
696
724
  * Il glifo del tipo. Per un tipo che il dizionario del backend aggiunge e questa mappa non
@@ -946,6 +974,137 @@ function checkConditionLogic(logic, conditionCount) {
946
974
  return null;
947
975
  }
948
976
 
977
+ /**
978
+ * Testo e numero non si confrontano — FRONTEND.md §4.3, §13.13.
979
+ *
980
+ * `Importo > "1000"` con `Importo` numerico e' `CONDITION_TYPE_MISMATCH`, un **errore** che
981
+ * blocca l'attivazione: la conversione implicita renderebbe l'esito dipendente dal formato
982
+ * del testo, e chi disegna il flow non ha modo di accorgersene.
983
+ *
984
+ * Il backend segnala solo quando conosce **entrambi** i tipi, e su un campo di un record o
985
+ * sull'output automatico di un elemento non li conosce: il posto giusto in cui fermare
986
+ * l'utente e' il picker, cioe' l'editor delle condizioni. Questo file e' la regola, in un
987
+ * posto solo.
988
+ *
989
+ * Vale per i soli operatori di uguaglianza e ordine. `Contains`, `StartsWith`, `EndsWith`,
990
+ * `In` e `NotIn` hanno una semantica propria e non sono soggetti alla regola; gli unari non
991
+ * hanno un secondo operando.
992
+ */
993
+ /** Gli operatori con una semantica propria: la regola non li riguarda (§4.3). */
994
+ const OPERATORS_WITHOUT_TYPE_CHECK = new Set([
995
+ 'Contains',
996
+ 'StartsWith',
997
+ 'EndsWith',
998
+ 'In',
999
+ 'NotIn',
1000
+ 'None',
1001
+ ]);
1002
+ /** `Integer` e `Number` sono intercambiabili fra loro. */
1003
+ const NUMERIC_TYPES = new Set(['Integer', 'Number']);
1004
+ /**
1005
+ * `true` → per questo operatore i due lati devono essere compatibili.
1006
+ * `isUnary` arriva dal dizionario: un unario non ha un termine di confronto.
1007
+ */
1008
+ function isTypeCheckedOperator(operator, isUnary) {
1009
+ if (!operator || isUnary) {
1010
+ return false;
1011
+ }
1012
+ return !OPERATORS_WITHOUT_TYPE_CHECK.has(operator);
1013
+ }
1014
+ function isNumericType(dataType) {
1015
+ return !!dataType && NUMERIC_TYPES.has(dataType);
1016
+ }
1017
+ /**
1018
+ * `false` solo per la coppia vietata: testo con numero.
1019
+ *
1020
+ * È l'**unica** coppia vietata. Un `Enum` confrontato con un testo resta lecito, perche' un
1021
+ * enum si confronta anche per nome; e con un tipo ignoto da un lato non si segnala nulla,
1022
+ * perche' non sapere non e' sapere che e' sbagliato.
1023
+ */
1024
+ function areTypesComparable(left, right) {
1025
+ if (!left || !right) {
1026
+ return true;
1027
+ }
1028
+ if (isNumericType(left) && right === 'String') {
1029
+ return false;
1030
+ }
1031
+ if (left === 'String' && isNumericType(right)) {
1032
+ return false;
1033
+ }
1034
+ return true;
1035
+ }
1036
+ /**
1037
+ * I numeri viaggiano in **cultura invariante**: il separatore decimale e' il punto, la virgola
1038
+ * non e' accettata (§4.3). Un campo che mostra `1.234,50` deve scrivere `1234.50`.
1039
+ *
1040
+ * Accetta la virgola come separatore decimale digitato — e' quello che un utente italiano
1041
+ * scrive — e la traduce, invece di lasciare che `parseFloat` tronchi silenziosamente a `1`.
1042
+ */
1043
+ function parseInvariantNumber(raw) {
1044
+ const trimmed = raw.trim();
1045
+ if (!trimmed) {
1046
+ return undefined;
1047
+ }
1048
+ // Una sola virgola e nessun punto: e' il separatore decimale.
1049
+ const normalized = !trimmed.includes('.') && (trimmed.match(/,/g) ?? []).length === 1
1050
+ ? trimmed.replace(',', '.')
1051
+ : trimmed;
1052
+ if (!/^[+-]?(\d+(\.\d*)?|\.\d+)([eE][+-]?\d+)?$/.test(normalized)) {
1053
+ return undefined;
1054
+ }
1055
+ const parsed = Number(normalized);
1056
+ return Number.isFinite(parsed) ? parsed : undefined;
1057
+ }
1058
+
1059
+ /**
1060
+ * Gli step di uno stage di orchestrazione — FRONTEND.md §5.13.
1061
+ *
1062
+ * Tre regole del contratto che non si vedono guardando il tipo, e che qui stanno in un posto
1063
+ * solo perche' le usano l'inspector, lo spazio dei nomi del documento e la validazione locale:
1064
+ *
1065
+ * 1. il `name` di uno step vive nello **stesso spazio dei nomi** di node e risorse (§3.3):
1066
+ * uno step omonimo di una variabile e' `NAME_DUPLICATED`;
1067
+ * 2. l'output di uno step e' sempre referenziabile come `<NomeStep>.<NomeOutput>`, ma una
1068
+ * condizione **non** puo' referenziarlo: finche' lo step non ha girato il riferimento e'
1069
+ * irrisolvibile, e a runtime un riferimento irrisolvibile e' un errore;
1070
+ * 3. gli step non sono una sequenza: l'ordine nell'array e' solo l'ordine d'esame.
1071
+ */
1072
+ /** I nomi di tutti gli step del documento: entrano nello spazio dei nomi comune (§3.3). */
1073
+ function stageStepNames(definition) {
1074
+ const names = [];
1075
+ for (const stage of definition.orchestratedStages ?? []) {
1076
+ for (const step of stage.stageSteps ?? []) {
1077
+ if (step.name) {
1078
+ names.push(step.name);
1079
+ }
1080
+ }
1081
+ }
1082
+ return names;
1083
+ }
1084
+ /** Gli step di uno stage, con l'array assente trattato come vuoto. */
1085
+ function stepsOf(stage) {
1086
+ return stage?.stageSteps ?? [];
1087
+ }
1088
+ /**
1089
+ * Il nome dello step il cui output un riferimento sta leggendo, se e' uno degli step passati.
1090
+ * `undefined` quando il riferimento e' legittimo. Serve a impedire nell'editor la condizione
1091
+ * che referenzia l'output di un altro step (§5.13, punto 4): la strada corretta e' scrivere
1092
+ * quel risultato in una variabile con `outputParameters.assignToReference` e condizionare
1093
+ * su quella.
1094
+ */
1095
+ function stageStepOutputReferenced(reference, steps) {
1096
+ if (!reference) {
1097
+ return undefined;
1098
+ }
1099
+ const root = referenceRoot(reference.trim());
1100
+ return steps.find((step) => !!step.name && step.name === root)?.name;
1101
+ }
1102
+ /**
1103
+ * L'unico output che il runtime legge da un evaluation flow di condizione. Dichiararne altri
1104
+ * e' `STAGE_ACTION_INVALID` (§5.13, punto 7).
1105
+ */
1106
+ const ORCHESTRATION_CONDITION_OUTPUT = 'isOrchestrationConditionMet';
1107
+
949
1108
  /**
950
1109
  * Lo store del documento in lavorazione.
951
1110
  *
@@ -1020,13 +1179,15 @@ class FlowDocumentStore {
1020
1179
  return result;
1021
1180
  }, ...(ngDevMode ? [{ debugName: "resources" }] : []));
1022
1181
  /**
1023
- * §3.3 — l'insieme dei nomi già usati: node **e** risorse, un unico spazio di nomi.
1024
- * Il controllo di unicita' che guarda solo le variabili lascia passare una variabile
1025
- * omonima di un node (§13.5).
1182
+ * §3.3 — l'insieme dei nomi già usati: node, risorse **e** step di orchestrazione, un unico
1183
+ * spazio di nomi. Il controllo di unicita' che guarda solo le variabili lascia passare una
1184
+ * variabile omonima di un node (§13.5); quello che dimentica gli step lascia passare uno
1185
+ * step omonimo di una variabile, che e' `NAME_DUPLICATED` allo stesso modo (§5.13).
1026
1186
  */
1027
1187
  usedNames = computed(() => [
1028
1188
  ...this.nodes().map((reference) => reference.name),
1029
1189
  ...this.resources().map((reference) => reference.name),
1190
+ ...stageStepNames(this._document()),
1030
1191
  ], ...(ngDevMode ? [{ debugName: "usedNames" }] : []));
1031
1192
  /** Gli archi derivati dal documento, Start incluso. */
1032
1193
  edges = computed(() => {
@@ -1418,10 +1579,15 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.27", ngImpo
1418
1579
  /**
1419
1580
  * Cache dei dizionari e dei cataloghi — FRONTEND.md §6.4.
1420
1581
  *
1421
- * I dizionari sono statici: si caricano una volta all'avvio dell'editor e si tengono in
1422
- * cache. I cataloghi (oggetti, campi, action, form, script, eventi) non lo sono, ma
1423
- * cambiano raramente: qui vengono memoizzati per chiave, in modo che aprire dieci volte lo
1424
- * stesso inspector non produca dieci richieste.
1582
+ * I dizionari sono **quasi** statici: si caricano una volta all'avvio dell'editor e si tengono
1583
+ * in cache, con un'eccezione `globalVariables` dipende dal `processType` con cui li si e'
1584
+ * chiesti, perche' `$Record` non esiste in uno screen flow (§4.1). Qui la cache e' quindi per
1585
+ * tipo di flow: cambiare `processType` nell'editor ricarica le globali, non tutto il resto
1586
+ * dell'esperienza.
1587
+ *
1588
+ * I cataloghi (oggetti, campi, action, form, script, eventi) cambiano raramente: sono
1589
+ * memoizzati per chiave, in modo che aprire dieci volte lo stesso inspector non produca dieci
1590
+ * richieste.
1425
1591
  *
1426
1592
  * Nessun elenco di enum e' cablato: se il backend aggiunge un operatore, appare nelle
1427
1593
  * tendine senza toccare il frontend (§13.11).
@@ -1430,24 +1596,41 @@ class FlowDictionaryStore {
1430
1596
  api = inject(FlowBuilderApi);
1431
1597
  _dictionaries = signal({}, ...(ngDevMode ? [{ debugName: "_dictionaries" }] : []));
1432
1598
  _isLoaded = signal(false, ...(ngDevMode ? [{ debugName: "_isLoaded" }] : []));
1433
- loading;
1599
+ _processType = signal(undefined, ...(ngDevMode ? [{ debugName: "_processType" }] : []));
1600
+ /** Una copia per tipo di flow: le globali dipendono dal `processType` (§4.1, §6.4). */
1601
+ cache = new Map();
1602
+ /** Cresce a ogni `load`: scarta la risposta di un caricamento superato. */
1603
+ sequence = 0;
1434
1604
  dictionaries = this._dictionaries.asReadonly();
1435
1605
  isLoaded = this._isLoaded.asReadonly();
1436
- /** Idempotente: chiamabile da ogni componente che ne ha bisogno. */
1437
- async load() {
1438
- if (this._isLoaded()) {
1606
+ /** Il `processType` dei dizionari attualmente in uso. */
1607
+ processType = this._processType.asReadonly();
1608
+ /**
1609
+ * Idempotente: chiamabile da ogni componente che ne ha bisogno.
1610
+ * Con un `processType` diverso da quello in uso ricarica — dalla cache se c'e' già —
1611
+ * perche' cambiare tipo di flow cambia quali globali sono proponibili.
1612
+ */
1613
+ async load(processType) {
1614
+ if (this._isLoaded() && processType === this._processType()) {
1439
1615
  return;
1440
1616
  }
1441
- this.loading ??= this.api
1442
- .getDictionaries()
1443
- .then((dictionaries) => {
1444
- this._dictionaries.set(dictionaries ?? {});
1445
- this._isLoaded.set(true);
1446
- })
1447
- .finally(() => {
1448
- this.loading = undefined;
1449
- });
1450
- return this.loading;
1617
+ const key = processType ?? '';
1618
+ const token = ++this.sequence;
1619
+ let entry = this.cache.get(key);
1620
+ if (!entry) {
1621
+ entry = this.api.getDictionaries(processType).then((dictionaries) => dictionaries ?? {});
1622
+ // Un errore non va memoizzato: al prossimo tentativo si riprova.
1623
+ entry.catch(() => this.cache.delete(key));
1624
+ this.cache.set(key, entry);
1625
+ }
1626
+ const dictionaries = await entry;
1627
+ if (token !== this.sequence) {
1628
+ // Un caricamento piu' recente ha già applicato un altro tipo: questo e' superato.
1629
+ return;
1630
+ }
1631
+ this._dictionaries.set(dictionaries);
1632
+ this._processType.set(processType);
1633
+ this._isLoaded.set(true);
1451
1634
  }
1452
1635
  // -------------------------------------------------------------------------
1453
1636
  // Accessori tipizzati
@@ -1468,6 +1651,8 @@ class FlowDictionaryStore {
1468
1651
  transformTypes = computed(() => this._dictionaries().transformTypes ?? [], ...(ngDevMode ? [{ debugName: "transformTypes" }] : []));
1469
1652
  transactionModels = computed(() => this._dictionaries().transactionModels ?? [], ...(ngDevMode ? [{ debugName: "transactionModels" }] : []));
1470
1653
  waitEventTypes = computed(() => this._dictionaries().waitEventTypes ?? [], ...(ngDevMode ? [{ debugName: "waitEventTypes" }] : []));
1654
+ stageStepTypes = computed(() => this._dictionaries().stageStepTypes ?? [], ...(ngDevMode ? [{ debugName: "stageStepTypes" }] : []));
1655
+ assigneeTypes = computed(() => this._dictionaries().assigneeTypes ?? [], ...(ngDevMode ? [{ debugName: "assigneeTypes" }] : []));
1471
1656
  conditionLogicModes = computed(() => this._dictionaries().conditionLogicModes ?? [], ...(ngDevMode ? [{ debugName: "conditionLogicModes" }] : []));
1472
1657
  elementTypes = computed(() => this._dictionaries().elementTypes ?? [], ...(ngDevMode ? [{ debugName: "elementTypes" }] : []));
1473
1658
  /**
@@ -1551,6 +1736,50 @@ class FlowDictionaryStore {
1551
1736
  }
1552
1737
  return operators.filter((entry) => !entry.appliesTo || entry.appliesTo.length === 0 || entry.appliesTo.includes(dataType));
1553
1738
  }
1739
+ /** La voce di `globalVariables` di uno scope: `$User`, `$Flow`, … (§4.1). */
1740
+ globalScope(scope) {
1741
+ if (!scope) {
1742
+ return undefined;
1743
+ }
1744
+ return this.globalVariables().find((entry) => entry.scope === scope || entry.value === scope);
1745
+ }
1746
+ /**
1747
+ * §4.1 — la verificabilita' e' **per scope**: se lo scope dichiara almeno un percorso, uno
1748
+ * che non c'e' e' `GLOBAL_UNKNOWN`; se non ne dichiara nessuno, resta non verificabile, che
1749
+ * significa "non lo so", non "non esiste".
1750
+ */
1751
+ isGlobalScopeVerifiable(scope) {
1752
+ return (this.globalScope(scope)?.paths?.length ?? 0) > 0;
1753
+ }
1754
+ /** I percorsi noti di uno scope, già prefissati: `$User.Email`. */
1755
+ globalPathsOf(scope) {
1756
+ const entry = this.globalScope(scope);
1757
+ if (!entry) {
1758
+ return [];
1759
+ }
1760
+ const prefix = entry.scope ?? entry.value;
1761
+ return (entry.paths ?? []).map((path) => `${prefix}.${path}`);
1762
+ }
1763
+ /** §5.13 — la voce di `stageStepTypes` di un `actionType`. */
1764
+ stageStepType(value) {
1765
+ if (!value) {
1766
+ return undefined;
1767
+ }
1768
+ return this.stageStepTypes().find((entry) => entry.value === value);
1769
+ }
1770
+ /** `stepBackground` vuole `actionName`; il fallback serve se il dizionario non lo dichiara. */
1771
+ stageStepRequiresActionName(actionType) {
1772
+ const entry = this.stageStepType(actionType);
1773
+ return entry?.requiresActionName ?? actionType === 'stepBackground';
1774
+ }
1775
+ /** `stepInteractive` e `stepApproval` vogliono gli assegnatari (§5.13). */
1776
+ stageStepRequiresAssignees(actionType) {
1777
+ const entry = this.stageStepType(actionType);
1778
+ if (entry?.requiresAssignees !== undefined) {
1779
+ return entry.requiresAssignees;
1780
+ }
1781
+ return actionType === 'stepInteractive' || actionType === 'stepApproval';
1782
+ }
1554
1783
  dataType(value) {
1555
1784
  return this.dataTypes().find((entry) => entry.value === value);
1556
1785
  }
@@ -2512,6 +2741,11 @@ class FlowCanvasComponent {
2512
2741
  }
2513
2742
  case 'Transform':
2514
2743
  return node['objectType'] || null;
2744
+ case 'OrchestratedStage': {
2745
+ // Gli step non sono una sequenza: si conta quanti sono, non fino a dove si e' arrivati.
2746
+ const steps = node['stageSteps']?.length ?? 0;
2747
+ return steps ? `${steps} step` : null;
2748
+ }
2515
2749
  case 'CustomError': {
2516
2750
  const messages = node['customErrorMessages']?.length ?? 0;
2517
2751
  return messages ? `${messages} ${messages === 1 ? 'messaggio' : 'messaggi'}` : null;
@@ -2659,11 +2893,11 @@ class FlowCanvasComponent {
2659
2893
  return 'cat-other';
2660
2894
  }
2661
2895
  static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.3.27", ngImport: i0, type: FlowCanvasComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
2662
- static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "20.3.27", type: FlowCanvasComponent, isStandalone: true, selector: "fb-flow-canvas", inputs: { selectedName: { classPropertyName: "selectedName", publicName: "selectedName", isSignal: true, isRequired: false, transformFunction: null }, outline: { classPropertyName: "outline", publicName: "outline", isSignal: true, isRequired: false, transformFunction: null } }, outputs: { selectionChange: "selectionChange", nodeOpened: "nodeOpened", nodeRemoveRequested: "nodeRemoveRequested", nodeDuplicateRequested: "nodeDuplicateRequested", elementDropped: "elementDropped" }, ngImport: i0, template: "<!--\n Gerarchia obbligatoria: f-flow > f-canvas > fNode / f-connection.\n I `@for` sono direttamente dentro <f-canvas>: nessun wrapper, quindi non serve\n `ngProjectAs` (che sarebbe indispensabile con blocchi annidati).\n-->\n<f-flow\n fDraggable\n (fCreateConnection)=\"onCreateConnection($event)\"\n (fReassignConnection)=\"onReassignConnection($event)\"\n (fMoveNodes)=\"onMoveNodes($event)\"\n (fSelectionChange)=\"onSelectionChange($event)\"\n (fDeleteSelected)=\"onDeleteSelected($event)\"\n (fCreateNode)=\"onCreateNode($event)\"\n>\n <f-canvas fZoom>\n <f-background>\n <f-circle-pattern />\n </f-background>\n\n @for (edge of edges(); track edge.id) {\n <f-connection\n [fConnectionId]=\"edge.id\"\n [fSourceId]=\"edge.sourceId\"\n [fTargetId]=\"edge.targetId\"\n fBehavior=\"floating\"\n [fType]=\"edge.isGoTo ? 'segment' : 'bezier'\"\n [class]=\"'fb-edge fb-edge--' + edge.kind + (edge.isGoTo ? ' fb-edge--goto' : '')\"\n >\n <f-connection-marker-arrow [type]=\"markerEnd\" />\n @if (edge.label) {\n <div fConnectionContent class=\"fb-edge-label\">{{ edge.label }}</div>\n }\n </f-connection>\n }\n\n @for (node of nodes(); track node.id) {\n <div\n fNode\n fDragHandle\n [fNodeId]=\"node.id\"\n [fNodePosition]=\"node.position\"\n [class]=\"'fb-node ' + categoryClass(node) + ' ' + node.widthClass\"\n [class.fb-node--start]=\"node.isStart\"\n [class.fb-node--selected]=\"isSelected(node)\"\n [class.fb-node--unreachable]=\"!node.isReachable\"\n [class.fb-node--error]=\"node.severity === 'Error'\"\n [class.fb-node--warning]=\"node.severity === 'Warning'\"\n (dblclick)=\"onNodeDoubleClick(node.name)\"\n >\n <!--\n Lo Start non ha ingresso: e' il punto di partenza (\u00A73.4).\n `fConnectorConnectableSide` e' cio' che fa entrare l'arco dall'alto: senza, foblex\n calcola il lato e un arco che scende dal node sopra potrebbe agganciarsi di fianco.\n -->\n @if (!node.isStart) {\n <div\n fConnector\n fConnectorType=\"target\"\n [fConnectorId]=\"targetIdOf(node)\"\n [fConnectorConnectableSide]=\"sides.TOP\"\n fConnectorMultiple=\"true\"\n class=\"fb-connector fb-connector--in\"\n title=\"Ingresso\"\n ></div>\n }\n\n <div class=\"fb-node__head\">\n <span class=\"fb-node__icon\" aria-hidden=\"true\">{{ node.icon }}</span>\n <div class=\"fb-node__text\">\n <span class=\"fb-node__title\" [title]=\"node.description || node.label\">{{ node.label }}</span>\n <span class=\"fb-node__sub\">\n {{ node.typeLabel }}\n @if (node.subtitle) {\n <span class=\"fb-node__sub-dot\">\u00B7</span>{{ node.subtitle }}\n }\n </span>\n </div>\n @if (node.hasAutomaticOutput) {\n <!-- L'elemento espone il proprio risultato sotto il proprio nome (\u00A74.5). -->\n <span class=\"fb-node__auto\" title=\"Espone un output automatico referenziabile come \u00AB{{ node.name }}\u00BB\">\n \u0192\n </span>\n }\n <!--\n `fDragBlocker` impedisce che il pointerdown sul bottone diventi un trascinamento\n del node: senza, aprire il dettaglio sposterebbe l'elemento di qualche pixel.\n -->\n <button\n type=\"button\"\n fDragBlocker\n class=\"fb-node__edit\"\n [attr.aria-label]=\"'Apri il dettaglio di ' + node.label\"\n title=\"Apri il dettaglio\"\n (click)=\"onEditClick($event, node.name)\"\n >\n \u270E\n </button>\n </div>\n\n <div class=\"fb-node__meta\">\n @if (!node.isStart) {\n <span class=\"fb-node__name\" [title]=\"'Nome tecnico: ' + node.name\">{{ node.name }}</span>\n }\n @if (!node.isReachable) {\n <span class=\"fb-badge fb-badge--unreachable\" title=\"Nessun percorso raggiunge questo elemento dallo Start\">\n scollegato\n </span>\n }\n @if (node.issueCount > 0) {\n <span\n class=\"fb-badge\"\n [class.fb-badge--error]=\"node.severity === 'Error'\"\n [class.fb-badge--warning]=\"node.severity === 'Warning'\"\n [class.fb-badge--info]=\"node.severity === 'Info'\"\n [title]=\"node.issueCount + ' rilievi di validazione'\"\n >\n {{ node.issueCount }}\n </span>\n }\n @if (node.danglingOutlets.length > 0) {\n <span\n class=\"fb-badge fb-badge--dangling\"\n [title]=\"'Rami dichiarati senza destinazione: ' + danglingLabels(node)\"\n >\n ramo incompleto\n </span>\n }\n </div>\n\n <!--\n Le uscite stanno sul bordo **inferiore**, una per ramo, nell'ordine in cui il modello\n le dichiara: per una Decision e' l'ordine di valutazione delle regole, che e'\n semantico (\u00A75.3), e da sinistra a destra si legge come la lista nell'inspector.\n L'etichetta si mostra solo quando i rami sono piu' di uno: su un `next` unico\n direbbe soltanto \u00ABSuccessivo\u00BB.\n -->\n <div class=\"fb-node__outlets\" [class.fb-node__outlets--labelled]=\"node.showsOutletLabels\">\n @for (outlet of node.outlets; track outlet.key) {\n <div class=\"fb-outlet\">\n @if (node.showsOutletLabels) {\n <span class=\"fb-outlet__label\" [title]=\"outlet.label\">{{ outlet.label }}</span>\n }\n <div\n fConnector\n fConnectorType=\"source\"\n [fConnectorId]=\"connectorIdOf(node, outlet.key)\"\n [fConnectorConnectableSide]=\"sides.BOTTOM\"\n [class]=\"'fb-connector fb-connector--out fb-connector--' + outlet.kind\"\n [title]=\"outlet.label\"\n ></div>\n </div>\n }\n </div>\n </div>\n }\n\n <!-- Anteprima dell'arco durante il trascinamento. -->\n <f-connection-for-create fBehavior=\"floating\" fType=\"bezier\" class=\"fb-edge fb-edge--creating\">\n <f-connection-marker-arrow [type]=\"markerEnd\" />\n </f-connection-for-create>\n\n <f-selection-area />\n </f-canvas>\n\n <f-minimap [fMinSize]=\"1200\" class=\"fb-minimap\" />\n</f-flow>\n", styles: [":host{display:block;position:relative;width:100%;height:100%;overflow:hidden;background:var(--fb-canvas-bg, #f4f5f7)}f-flow{display:block;width:100%;height:100%}.fb-minimap{position:absolute;right:14px;bottom:14px;width:190px;height:130px;border:1px solid var(--fb-border, #e2e5eb);border-radius:var(--fb-radius, 10px);background:var(--fb-surface, #fff);box-shadow:var(--fb-shadow-md, 0 6px 18px rgb(16 24 40 / 10%));overflow:hidden}.fb-node{position:absolute;display:flex;flex-direction:column;box-sizing:border-box;width:240px;padding:0;border:1px solid var(--fb-border, #e2e5eb);border-radius:var(--fb-radius-lg, 12px);background:var(--fb-surface, #fff);box-shadow:var(--fb-shadow-sm, 0 1px 2px rgb(16 24 40 / 6%));font:inherit;cursor:grab;-webkit-user-select:none;user-select:none;transition:box-shadow .12s ease,border-color .12s ease}.fb-node--outlets-3{width:304px}.fb-node--outlets-4{width:380px}.fb-node--outlets-5{width:456px}.fb-node--outlets-6{width:520px}.fb-node:hover{box-shadow:var(--fb-shadow-md, 0 6px 18px rgb(16 24 40 / 10%))}.fb-node--selected{border-color:var(--fb-accent, #4f6ef7);box-shadow:0 0 0 3px color-mix(in srgb,var(--fb-accent, #4f6ef7) 22%,transparent)}.fb-node--unreachable{border-style:dashed;opacity:.8}.fb-node--error{border-color:var(--fb-error, #c9372c)}.fb-node--warning{border-color:var(--fb-warning, #b7791f)}.fb-node__head{display:flex;align-items:center;gap:8px;padding:10px 10px 6px 12px}.fb-node__icon{display:grid;place-items:center;flex:0 0 auto;width:28px;height:28px;border-radius:var(--fb-radius-sm, 8px);background:var(--fb-node-accent, #667085);color:#fff;font-size:14px;line-height:1}.cat-start{--fb-node-accent: #22a06b}.cat-screen{--fb-node-accent: #3b82f6}.cat-logic{--fb-node-accent: #8b5cf6}.cat-data{--fb-node-accent: #06b6d4}.cat-action{--fb-node-accent: #f59e0b}.cat-flow{--fb-node-accent: #14b8a6}.cat-other{--fb-node-accent: #667085}.fb-node__text{flex:1;min-width:0}.fb-node__title{display:block;font-size:13px;font-weight:600;line-height:17px;color:var(--fb-text, #1a1c23);overflow:hidden;text-overflow:ellipsis;white-space:nowrap}.fb-node__sub{display:block;margin-top:1px;font-size:11px;line-height:14px;color:var(--fb-text-muted, #6b7086);overflow:hidden;text-overflow:ellipsis;white-space:nowrap}.fb-node__sub-dot{margin:0 4px;color:var(--fb-text-subtle, #98a2b3)}.fb-node__auto{flex:0 0 auto;font-size:12px;font-weight:700;color:var(--fb-accent, #4f6ef7);cursor:help}.fb-node__edit{display:grid;place-items:center;flex:0 0 auto;width:22px;height:22px;padding:0;border:0;border-radius:var(--fb-radius-xs, 6px);background:transparent;color:var(--fb-text-subtle, #98a2b3);font:inherit;font-size:12px;cursor:pointer;opacity:0;transition:opacity .12s ease,background .12s ease}.fb-node:hover .fb-node__edit,.fb-node--selected .fb-node__edit,.fb-node__edit:focus-visible{opacity:1}.fb-node__edit:hover{background:var(--fb-surface-alt, #f7f8fa);color:var(--fb-text, #1a1c23)}.fb-node__meta{display:flex;flex-wrap:wrap;align-items:center;gap:4px;min-height:14px;padding:0 12px 6px}.fb-node__name{flex:0 1 auto;font-family:ui-monospace,SFMono-Regular,Menlo,monospace;font-size:10px;color:var(--fb-text-subtle, #98a2b3);overflow:hidden;text-overflow:ellipsis;white-space:nowrap}.fb-badge{padding:1px 6px;border-radius:10px;background:var(--fb-badge-bg, #eef0f4);font-size:10px;font-weight:600;color:var(--fb-text-muted, #6b7086);white-space:nowrap;cursor:help}.fb-badge--error{background:color-mix(in srgb,var(--fb-error, #c9372c) 14%,transparent);color:var(--fb-error, #c9372c)}.fb-badge--warning{background:color-mix(in srgb,var(--fb-warning, #b7791f) 16%,transparent);color:var(--fb-warning, #b7791f)}.fb-badge--info{background:color-mix(in srgb,var(--fb-accent, #4f6ef7) 12%,transparent);color:var(--fb-accent, #4f6ef7)}.fb-badge--unreachable,.fb-badge--dangling{background:color-mix(in srgb,var(--fb-warning, #b7791f) 12%,transparent);color:var(--fb-warning, #b7791f)}.fb-node__outlets{display:flex;align-items:flex-end;justify-content:space-evenly;gap:4px;padding:0 8px 4px}.fb-node__outlets--labelled{padding-top:5px;border-top:1px solid var(--fb-border-subtle, #eef0f4)}.fb-outlet{display:flex;flex:1 1 0;flex-direction:column;align-items:center;gap:2px;min-width:0}.fb-outlet__label{max-width:100%;font-size:10px;line-height:13px;color:var(--fb-text-muted, #6b7086);text-align:center;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}.fb-connector{box-sizing:border-box;width:12px;height:12px;flex:0 0 auto;border:2px solid var(--fb-surface, #fff);border-radius:50%;background:var(--fb-connector, #98a2b3);cursor:crosshair;transition:transform .12s ease,box-shadow .12s ease}f-flow .fb-connector--out{position:relative;inset:auto;margin-bottom:-12px}.fb-connector--out:hover{transform:scale(1.2)}f-flow .fb-connector--in{position:absolute;inset:-2px auto auto 50%;width:24px;height:4px;border:0;border-radius:2px;transform:translate(-50%);background:var(--fb-border-strong, #cfd4de)}.fb-connector--out{--ff-connector-connected-color: var(--fb-connector, #98a2b3)}.fb-connector--Next,.fb-connector--Start,.fb-connector--LoopNext{--fb-connector: var(--fb-edge-next, #98a2b3)}.fb-connector--Rule,.fb-connector--WaitEvent{--fb-connector: var(--fb-edge-rule, #8b5cf6)}.fb-connector--Default,.fb-connector--LoopEnd{--fb-connector: var(--fb-edge-default, #06b6d4)}.fb-connector--Fault{--fb-connector: var(--fb-edge-fault, #dc2626)}.fb-connector--Timeout,.fb-connector--ScheduledPath{--fb-connector: var(--fb-edge-timeout, #f59e0b)}.fb-connector.f-connector-connectable{box-shadow:0 0 0 4px color-mix(in srgb,var(--fb-accent, #4f6ef7) 28%,transparent)}\n"], dependencies: [{ kind: "ngmodule", type: FFlowModule }, { kind: "component", type: i1.FFlowComponent, selector: "f-flow", inputs: ["fFlowId", "fCache"], outputs: ["fNodesRendered", "fFullRendered", "fLoaded"] }, { kind: "component", type: i1.FCanvasComponent, selector: "f-canvas", inputs: ["position", "scale", "debounceTime", "fLayers"], outputs: ["fCanvasChange"] }, { kind: "component", type: i1.FBackgroundComponent, selector: "f-background" }, { kind: "component", type: i1.FCirclePatternComponent, selector: "f-circle-pattern", inputs: ["id", "color", "radius"] }, { kind: "directive", type: i1.FZoomDirective, selector: "f-canvas[fZoom]", inputs: ["fZoom", "fWheelTrigger", "fDblClickTrigger", "fZoomMinimum", "fZoomMaximum", "fZoomStep", "fPinchStep", "fZoomDblClickStep"] }, { kind: "component", type: i1.FSelectionArea, selector: "f-selection-area", inputs: ["fTrigger"] }, { kind: "directive", type: i1.FConnectionContent, selector: "[fConnectionContent]", inputs: ["position", "offset", "align"] }, { kind: "component", type: i1.FConnectionMarkerArrow, selector: "f-connection-marker-arrow", inputs: ["type"] }, { kind: "component", type: i1.FConnectionComponent, selector: "f-connection", inputs: ["fConnectionId", "fSourceId", "fTargetId", "fOutputId", "fInputId", "fRadius", "fOffset", "fBehavior", "fType", "fSelectionDisabled", "fReassignableStart", "fReassignDisabled", "fSourceSide", "fTargetSide", "fInputSide", "fOutputSide"], exportAs: ["fComponent"] }, { kind: "component", type: i1.FConnectionForCreateComponent, selector: "f-connection-for-create", inputs: ["fRadius", "fOffset", "fBehavior", "fType", "fInputSide", "fOutputSide"] }, { kind: "directive", type: i1.FConnectorDirective, selector: "[fConnector]", inputs: ["fConnectorId", "fConnectorType", "fConnectorDisabled", "fConnectorMultiple", "fConnectorCategory", "fConnectorConnectableSide", "fConnectorSelfConnectable", "fCanBeConnectedTo", "fConnectionFromOutlet"], exportAs: ["fConnector"] }, { kind: "component", type: i1.FMinimapComponent, selector: "f-minimap", inputs: ["fMinSize", "fNodeRenderLimit"], exportAs: ["fComponent"] }, { kind: "directive", type: i1.FNodeDirective, selector: "[fNode]", inputs: ["fNodeId", "fNodeParentId", "fNodePosition", "fNodeSize", "fNodeRotate", "fConnectOnNode", "fMinimapClass", "fNodeDraggingDisabled", "fNodeSelectionDisabled", "fIncludePadding", "fAutoExpandOnChildHit", "fAutoSizeToFitChildren"], outputs: ["fNodePositionChange", "fNodeSizeChange", "fNodeRotateChange"], exportAs: ["fComponent"] }, { kind: "directive", type: i1.FDragHandleDirective, selector: "[fDragHandle]" }, { kind: "directive", type: i1.FDragBlockerDirective, selector: "[fDragBlocker]" }, { kind: "directive", type: i1.FDraggableDirective, selector: "f-flow[fDraggable]", inputs: ["fDraggableDisabled", "fDropToGroup", "fMultiSelectTrigger", "fReassignConnectionTrigger", "fCreateConnectionTrigger", "fConnectionWaypointsTrigger", "fMoveControlPointTrigger", "fNodeResizeTrigger", "fNodeRotateTrigger", "fNodeMoveTrigger", "fCanvasMoveTrigger", "fExternalItemTrigger", "fEmitOnNodeIntersect", "vCellSize", "hCellSize", "fCellSizeWhileDragging"], outputs: ["fSelectionChange", "fDeleteSelected", "fNodeIntersectedWithConnections", "fNodeConnectionsIntersection", "fCreateNode", "fMoveNodes", "fReassignConnection", "fCreateConnection", "fConnectionWaypointsChanged", "fDropToGroup", "fDragStarted", "fDragEnded"], exportAs: ["fDraggable"] }], changeDetection: i0.ChangeDetectionStrategy.OnPush });
2896
+ static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "20.3.27", type: FlowCanvasComponent, isStandalone: true, selector: "fb-flow-canvas", inputs: { selectedName: { classPropertyName: "selectedName", publicName: "selectedName", isSignal: true, isRequired: false, transformFunction: null }, outline: { classPropertyName: "outline", publicName: "outline", isSignal: true, isRequired: false, transformFunction: null } }, outputs: { selectionChange: "selectionChange", nodeOpened: "nodeOpened", nodeRemoveRequested: "nodeRemoveRequested", nodeDuplicateRequested: "nodeDuplicateRequested", elementDropped: "elementDropped" }, ngImport: i0, template: "<!--\r\n Gerarchia obbligatoria: f-flow > f-canvas > fNode / f-connection.\r\n I `@for` sono direttamente dentro <f-canvas>: nessun wrapper, quindi non serve\r\n `ngProjectAs` (che sarebbe indispensabile con blocchi annidati).\r\n-->\r\n<f-flow\r\n fDraggable\r\n (fCreateConnection)=\"onCreateConnection($event)\"\r\n (fReassignConnection)=\"onReassignConnection($event)\"\r\n (fMoveNodes)=\"onMoveNodes($event)\"\r\n (fSelectionChange)=\"onSelectionChange($event)\"\r\n (fDeleteSelected)=\"onDeleteSelected($event)\"\r\n (fCreateNode)=\"onCreateNode($event)\"\r\n>\r\n <f-canvas fZoom>\r\n <f-background>\r\n <f-circle-pattern />\r\n </f-background>\r\n\r\n @for (edge of edges(); track edge.id) {\r\n <f-connection\r\n [fConnectionId]=\"edge.id\"\r\n [fSourceId]=\"edge.sourceId\"\r\n [fTargetId]=\"edge.targetId\"\r\n fBehavior=\"floating\"\r\n [fType]=\"edge.isGoTo ? 'segment' : 'bezier'\"\r\n [class]=\"'fb-edge fb-edge--' + edge.kind + (edge.isGoTo ? ' fb-edge--goto' : '')\"\r\n >\r\n <f-connection-marker-arrow [type]=\"markerEnd\" />\r\n @if (edge.label) {\r\n <div fConnectionContent class=\"fb-edge-label\">{{ edge.label }}</div>\r\n }\r\n </f-connection>\r\n }\r\n\r\n @for (node of nodes(); track node.id) {\r\n <div\r\n fNode\r\n fDragHandle\r\n [fNodeId]=\"node.id\"\r\n [fNodePosition]=\"node.position\"\r\n [class]=\"'fb-node ' + categoryClass(node) + ' ' + node.widthClass\"\r\n [class.fb-node--start]=\"node.isStart\"\r\n [class.fb-node--selected]=\"isSelected(node)\"\r\n [class.fb-node--unreachable]=\"!node.isReachable\"\r\n [class.fb-node--error]=\"node.severity === 'Error'\"\r\n [class.fb-node--warning]=\"node.severity === 'Warning'\"\r\n (dblclick)=\"onNodeDoubleClick(node.name)\"\r\n >\r\n <!--\r\n Lo Start non ha ingresso: e' il punto di partenza (\u00A73.4).\r\n `fConnectorConnectableSide` e' cio' che fa entrare l'arco dall'alto: senza, foblex\r\n calcola il lato e un arco che scende dal node sopra potrebbe agganciarsi di fianco.\r\n -->\r\n @if (!node.isStart) {\r\n <div\r\n fConnector\r\n fConnectorType=\"target\"\r\n [fConnectorId]=\"targetIdOf(node)\"\r\n [fConnectorConnectableSide]=\"sides.TOP\"\r\n fConnectorMultiple=\"true\"\r\n class=\"fb-connector fb-connector--in\"\r\n title=\"Ingresso\"\r\n ></div>\r\n }\r\n\r\n <div class=\"fb-node__head\">\r\n <span class=\"fb-node__icon\" aria-hidden=\"true\">{{ node.icon }}</span>\r\n <div class=\"fb-node__text\">\r\n <span class=\"fb-node__title\" [title]=\"node.description || node.label\">{{ node.label }}</span>\r\n <span class=\"fb-node__sub\">\r\n {{ node.typeLabel }}\r\n @if (node.subtitle) {\r\n <span class=\"fb-node__sub-dot\">\u00B7</span>{{ node.subtitle }}\r\n }\r\n </span>\r\n </div>\r\n @if (node.hasAutomaticOutput) {\r\n <!-- L'elemento espone il proprio risultato sotto il proprio nome (\u00A74.5). -->\r\n <span class=\"fb-node__auto\" title=\"Espone un output automatico referenziabile come \u00AB{{ node.name }}\u00BB\">\r\n \u0192\r\n </span>\r\n }\r\n <!--\r\n `fDragBlocker` impedisce che il pointerdown sul bottone diventi un trascinamento\r\n del node: senza, aprire il dettaglio sposterebbe l'elemento di qualche pixel.\r\n -->\r\n <button\r\n type=\"button\"\r\n fDragBlocker\r\n class=\"fb-node__edit\"\r\n [attr.aria-label]=\"'Apri il dettaglio di ' + node.label\"\r\n title=\"Apri il dettaglio\"\r\n (click)=\"onEditClick($event, node.name)\"\r\n >\r\n \u270E\r\n </button>\r\n </div>\r\n\r\n <div class=\"fb-node__meta\">\r\n @if (!node.isStart) {\r\n <span class=\"fb-node__name\" [title]=\"'Nome tecnico: ' + node.name\">{{ node.name }}</span>\r\n }\r\n @if (!node.isReachable) {\r\n <span class=\"fb-badge fb-badge--unreachable\" title=\"Nessun percorso raggiunge questo elemento dallo Start\">\r\n scollegato\r\n </span>\r\n }\r\n @if (node.issueCount > 0) {\r\n <span\r\n class=\"fb-badge\"\r\n [class.fb-badge--error]=\"node.severity === 'Error'\"\r\n [class.fb-badge--warning]=\"node.severity === 'Warning'\"\r\n [class.fb-badge--info]=\"node.severity === 'Info'\"\r\n [title]=\"node.issueCount + ' rilievi di validazione'\"\r\n >\r\n {{ node.issueCount }}\r\n </span>\r\n }\r\n @if (node.danglingOutlets.length > 0) {\r\n <span\r\n class=\"fb-badge fb-badge--dangling\"\r\n [title]=\"'Rami dichiarati senza destinazione: ' + danglingLabels(node)\"\r\n >\r\n ramo incompleto\r\n </span>\r\n }\r\n </div>\r\n\r\n <!--\r\n Le uscite stanno sul bordo **inferiore**, una per ramo, nell'ordine in cui il modello\r\n le dichiara: per una Decision e' l'ordine di valutazione delle regole, che e'\r\n semantico (\u00A75.3), e da sinistra a destra si legge come la lista nell'inspector.\r\n L'etichetta si mostra solo quando i rami sono piu' di uno: su un `next` unico\r\n direbbe soltanto \u00ABSuccessivo\u00BB.\r\n -->\r\n <div class=\"fb-node__outlets\" [class.fb-node__outlets--labelled]=\"node.showsOutletLabels\">\r\n @for (outlet of node.outlets; track outlet.key) {\r\n <div class=\"fb-outlet\">\r\n @if (node.showsOutletLabels) {\r\n <span class=\"fb-outlet__label\" [title]=\"outlet.label\">{{ outlet.label }}</span>\r\n }\r\n <div\r\n fConnector\r\n fConnectorType=\"source\"\r\n [fConnectorId]=\"connectorIdOf(node, outlet.key)\"\r\n [fConnectorConnectableSide]=\"sides.BOTTOM\"\r\n [class]=\"'fb-connector fb-connector--out fb-connector--' + outlet.kind\"\r\n [title]=\"outlet.label\"\r\n ></div>\r\n </div>\r\n }\r\n </div>\r\n </div>\r\n }\r\n\r\n <!-- Anteprima dell'arco durante il trascinamento. -->\r\n <f-connection-for-create fBehavior=\"floating\" fType=\"bezier\" class=\"fb-edge fb-edge--creating\">\r\n <f-connection-marker-arrow [type]=\"markerEnd\" />\r\n </f-connection-for-create>\r\n\r\n <f-selection-area />\r\n </f-canvas>\r\n\r\n <f-minimap [fMinSize]=\"1200\" class=\"fb-minimap\" />\r\n</f-flow>\r\n", styles: [":host{display:block;position:relative;width:100%;height:100%;overflow:hidden;background:var(--fb-canvas-bg, #f4f5f7)}f-flow{display:block;width:100%;height:100%}.fb-minimap{position:absolute;right:14px;bottom:14px;width:190px;height:130px;border:1px solid var(--fb-border, #e2e5eb);border-radius:var(--fb-radius, 10px);background:var(--fb-surface, #fff);box-shadow:var(--fb-shadow-md, 0 6px 18px rgb(16 24 40 / 10%));overflow:hidden}.fb-node{position:absolute;display:flex;flex-direction:column;box-sizing:border-box;width:240px;padding:0;border:1px solid var(--fb-border, #e2e5eb);border-radius:var(--fb-radius-lg, 12px);background:var(--fb-surface, #fff);box-shadow:var(--fb-shadow-sm, 0 1px 2px rgb(16 24 40 / 6%));font:inherit;cursor:grab;-webkit-user-select:none;user-select:none;transition:box-shadow .12s ease,border-color .12s ease}.fb-node--outlets-3{width:304px}.fb-node--outlets-4{width:380px}.fb-node--outlets-5{width:456px}.fb-node--outlets-6{width:520px}.fb-node:hover{box-shadow:var(--fb-shadow-md, 0 6px 18px rgb(16 24 40 / 10%))}.fb-node--selected{border-color:var(--fb-accent, #4f6ef7);box-shadow:0 0 0 3px color-mix(in srgb,var(--fb-accent, #4f6ef7) 22%,transparent)}.fb-node--unreachable{border-style:dashed;opacity:.8}.fb-node--error{border-color:var(--fb-error, #c9372c)}.fb-node--warning{border-color:var(--fb-warning, #b7791f)}.fb-node__head{display:flex;align-items:center;gap:8px;padding:10px 10px 6px 12px}.fb-node__icon{display:grid;place-items:center;flex:0 0 auto;width:28px;height:28px;border-radius:var(--fb-radius-sm, 8px);background:var(--fb-node-accent, #667085);color:#fff;font-size:14px;line-height:1}.cat-start{--fb-node-accent: #22a06b}.cat-screen{--fb-node-accent: #3b82f6}.cat-logic{--fb-node-accent: #8b5cf6}.cat-data{--fb-node-accent: #06b6d4}.cat-action{--fb-node-accent: #f59e0b}.cat-flow{--fb-node-accent: #14b8a6}.cat-other{--fb-node-accent: #667085}.fb-node__text{flex:1;min-width:0}.fb-node__title{display:block;font-size:13px;font-weight:600;line-height:17px;color:var(--fb-text, #1a1c23);overflow:hidden;text-overflow:ellipsis;white-space:nowrap}.fb-node__sub{display:block;margin-top:1px;font-size:11px;line-height:14px;color:var(--fb-text-muted, #6b7086);overflow:hidden;text-overflow:ellipsis;white-space:nowrap}.fb-node__sub-dot{margin:0 4px;color:var(--fb-text-subtle, #98a2b3)}.fb-node__auto{flex:0 0 auto;font-size:12px;font-weight:700;color:var(--fb-accent, #4f6ef7);cursor:help}.fb-node__edit{display:grid;place-items:center;flex:0 0 auto;width:22px;height:22px;padding:0;border:0;border-radius:var(--fb-radius-xs, 6px);background:transparent;color:var(--fb-text-subtle, #98a2b3);font:inherit;font-size:12px;cursor:pointer;opacity:0;transition:opacity .12s ease,background .12s ease}.fb-node:hover .fb-node__edit,.fb-node--selected .fb-node__edit,.fb-node__edit:focus-visible{opacity:1}.fb-node__edit:hover{background:var(--fb-surface-alt, #f7f8fa);color:var(--fb-text, #1a1c23)}.fb-node__meta{display:flex;flex-wrap:wrap;align-items:center;gap:4px;min-height:14px;padding:0 12px 6px}.fb-node__name{flex:0 1 auto;font-family:ui-monospace,SFMono-Regular,Menlo,monospace;font-size:10px;color:var(--fb-text-subtle, #98a2b3);overflow:hidden;text-overflow:ellipsis;white-space:nowrap}.fb-badge{padding:1px 6px;border-radius:10px;background:var(--fb-badge-bg, #eef0f4);font-size:10px;font-weight:600;color:var(--fb-text-muted, #6b7086);white-space:nowrap;cursor:help}.fb-badge--error{background:color-mix(in srgb,var(--fb-error, #c9372c) 14%,transparent);color:var(--fb-error, #c9372c)}.fb-badge--warning{background:color-mix(in srgb,var(--fb-warning, #b7791f) 16%,transparent);color:var(--fb-warning, #b7791f)}.fb-badge--info{background:color-mix(in srgb,var(--fb-accent, #4f6ef7) 12%,transparent);color:var(--fb-accent, #4f6ef7)}.fb-badge--unreachable,.fb-badge--dangling{background:color-mix(in srgb,var(--fb-warning, #b7791f) 12%,transparent);color:var(--fb-warning, #b7791f)}.fb-node__outlets{display:flex;align-items:flex-end;justify-content:space-evenly;gap:4px;padding:0 8px 4px}.fb-node__outlets--labelled{padding-top:5px;border-top:1px solid var(--fb-border-subtle, #eef0f4)}.fb-outlet{display:flex;flex:1 1 0;flex-direction:column;align-items:center;gap:2px;min-width:0}.fb-outlet__label{max-width:100%;font-size:10px;line-height:13px;color:var(--fb-text-muted, #6b7086);text-align:center;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}.fb-connector{box-sizing:border-box;width:12px;height:12px;flex:0 0 auto;border:2px solid var(--fb-surface, #fff);border-radius:50%;background:var(--fb-connector, #98a2b3);cursor:crosshair;transition:transform .12s ease,box-shadow .12s ease}f-flow .fb-connector--out{position:relative;inset:auto;margin-bottom:-12px}.fb-connector--out:hover{transform:scale(1.2)}f-flow .fb-connector--in{position:absolute;inset:-2px auto auto 50%;width:24px;height:4px;border:0;border-radius:2px;transform:translate(-50%);background:var(--fb-border-strong, #cfd4de)}.fb-connector--out{--ff-connector-connected-color: var(--fb-connector, #98a2b3)}.fb-connector--Next,.fb-connector--Start,.fb-connector--LoopNext{--fb-connector: var(--fb-edge-next, #98a2b3)}.fb-connector--Rule,.fb-connector--WaitEvent{--fb-connector: var(--fb-edge-rule, #8b5cf6)}.fb-connector--Default,.fb-connector--LoopEnd{--fb-connector: var(--fb-edge-default, #06b6d4)}.fb-connector--Fault{--fb-connector: var(--fb-edge-fault, #dc2626)}.fb-connector--Timeout,.fb-connector--ScheduledPath{--fb-connector: var(--fb-edge-timeout, #f59e0b)}.fb-connector.f-connector-connectable{box-shadow:0 0 0 4px color-mix(in srgb,var(--fb-accent, #4f6ef7) 28%,transparent)}\n"], dependencies: [{ kind: "ngmodule", type: FFlowModule }, { kind: "component", type: i1.FFlowComponent, selector: "f-flow", inputs: ["fFlowId", "fCache"], outputs: ["fNodesRendered", "fFullRendered", "fLoaded"] }, { kind: "component", type: i1.FCanvasComponent, selector: "f-canvas", inputs: ["position", "scale", "debounceTime", "fLayers"], outputs: ["fCanvasChange"] }, { kind: "component", type: i1.FBackgroundComponent, selector: "f-background" }, { kind: "component", type: i1.FCirclePatternComponent, selector: "f-circle-pattern", inputs: ["id", "color", "radius"] }, { kind: "directive", type: i1.FZoomDirective, selector: "f-canvas[fZoom]", inputs: ["fZoom", "fWheelTrigger", "fDblClickTrigger", "fZoomMinimum", "fZoomMaximum", "fZoomStep", "fPinchStep", "fZoomDblClickStep"] }, { kind: "component", type: i1.FSelectionArea, selector: "f-selection-area", inputs: ["fTrigger"] }, { kind: "directive", type: i1.FConnectionContent, selector: "[fConnectionContent]", inputs: ["position", "offset", "align"] }, { kind: "component", type: i1.FConnectionMarkerArrow, selector: "f-connection-marker-arrow", inputs: ["type"] }, { kind: "component", type: i1.FConnectionComponent, selector: "f-connection", inputs: ["fConnectionId", "fSourceId", "fTargetId", "fOutputId", "fInputId", "fRadius", "fOffset", "fBehavior", "fType", "fSelectionDisabled", "fReassignableStart", "fReassignDisabled", "fSourceSide", "fTargetSide", "fInputSide", "fOutputSide"], exportAs: ["fComponent"] }, { kind: "component", type: i1.FConnectionForCreateComponent, selector: "f-connection-for-create", inputs: ["fRadius", "fOffset", "fBehavior", "fType", "fInputSide", "fOutputSide"] }, { kind: "directive", type: i1.FConnectorDirective, selector: "[fConnector]", inputs: ["fConnectorId", "fConnectorType", "fConnectorDisabled", "fConnectorMultiple", "fConnectorCategory", "fConnectorConnectableSide", "fConnectorSelfConnectable", "fCanBeConnectedTo", "fConnectionFromOutlet"], exportAs: ["fConnector"] }, { kind: "component", type: i1.FMinimapComponent, selector: "f-minimap", inputs: ["fMinSize", "fNodeRenderLimit"], exportAs: ["fComponent"] }, { kind: "directive", type: i1.FNodeDirective, selector: "[fNode]", inputs: ["fNodeId", "fNodeParentId", "fNodePosition", "fNodeSize", "fNodeRotate", "fConnectOnNode", "fMinimapClass", "fNodeDraggingDisabled", "fNodeSelectionDisabled", "fIncludePadding", "fAutoExpandOnChildHit", "fAutoSizeToFitChildren"], outputs: ["fNodePositionChange", "fNodeSizeChange", "fNodeRotateChange"], exportAs: ["fComponent"] }, { kind: "directive", type: i1.FDragHandleDirective, selector: "[fDragHandle]" }, { kind: "directive", type: i1.FDragBlockerDirective, selector: "[fDragBlocker]" }, { kind: "directive", type: i1.FDraggableDirective, selector: "f-flow[fDraggable]", inputs: ["fDraggableDisabled", "fDropToGroup", "fMultiSelectTrigger", "fReassignConnectionTrigger", "fCreateConnectionTrigger", "fConnectionWaypointsTrigger", "fMoveControlPointTrigger", "fNodeResizeTrigger", "fNodeRotateTrigger", "fNodeMoveTrigger", "fCanvasMoveTrigger", "fExternalItemTrigger", "fEmitOnNodeIntersect", "vCellSize", "hCellSize", "fCellSizeWhileDragging"], outputs: ["fSelectionChange", "fDeleteSelected", "fNodeIntersectedWithConnections", "fNodeConnectionsIntersection", "fCreateNode", "fMoveNodes", "fReassignConnection", "fCreateConnection", "fConnectionWaypointsChanged", "fDropToGroup", "fDragStarted", "fDragEnded"], exportAs: ["fDraggable"] }], changeDetection: i0.ChangeDetectionStrategy.OnPush });
2663
2897
  }
2664
2898
  i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.27", ngImport: i0, type: FlowCanvasComponent, decorators: [{
2665
2899
  type: Component,
2666
- args: [{ selector: 'fb-flow-canvas', standalone: true, imports: [FFlowModule], changeDetection: ChangeDetectionStrategy.OnPush, template: "<!--\n Gerarchia obbligatoria: f-flow > f-canvas > fNode / f-connection.\n I `@for` sono direttamente dentro <f-canvas>: nessun wrapper, quindi non serve\n `ngProjectAs` (che sarebbe indispensabile con blocchi annidati).\n-->\n<f-flow\n fDraggable\n (fCreateConnection)=\"onCreateConnection($event)\"\n (fReassignConnection)=\"onReassignConnection($event)\"\n (fMoveNodes)=\"onMoveNodes($event)\"\n (fSelectionChange)=\"onSelectionChange($event)\"\n (fDeleteSelected)=\"onDeleteSelected($event)\"\n (fCreateNode)=\"onCreateNode($event)\"\n>\n <f-canvas fZoom>\n <f-background>\n <f-circle-pattern />\n </f-background>\n\n @for (edge of edges(); track edge.id) {\n <f-connection\n [fConnectionId]=\"edge.id\"\n [fSourceId]=\"edge.sourceId\"\n [fTargetId]=\"edge.targetId\"\n fBehavior=\"floating\"\n [fType]=\"edge.isGoTo ? 'segment' : 'bezier'\"\n [class]=\"'fb-edge fb-edge--' + edge.kind + (edge.isGoTo ? ' fb-edge--goto' : '')\"\n >\n <f-connection-marker-arrow [type]=\"markerEnd\" />\n @if (edge.label) {\n <div fConnectionContent class=\"fb-edge-label\">{{ edge.label }}</div>\n }\n </f-connection>\n }\n\n @for (node of nodes(); track node.id) {\n <div\n fNode\n fDragHandle\n [fNodeId]=\"node.id\"\n [fNodePosition]=\"node.position\"\n [class]=\"'fb-node ' + categoryClass(node) + ' ' + node.widthClass\"\n [class.fb-node--start]=\"node.isStart\"\n [class.fb-node--selected]=\"isSelected(node)\"\n [class.fb-node--unreachable]=\"!node.isReachable\"\n [class.fb-node--error]=\"node.severity === 'Error'\"\n [class.fb-node--warning]=\"node.severity === 'Warning'\"\n (dblclick)=\"onNodeDoubleClick(node.name)\"\n >\n <!--\n Lo Start non ha ingresso: e' il punto di partenza (\u00A73.4).\n `fConnectorConnectableSide` e' cio' che fa entrare l'arco dall'alto: senza, foblex\n calcola il lato e un arco che scende dal node sopra potrebbe agganciarsi di fianco.\n -->\n @if (!node.isStart) {\n <div\n fConnector\n fConnectorType=\"target\"\n [fConnectorId]=\"targetIdOf(node)\"\n [fConnectorConnectableSide]=\"sides.TOP\"\n fConnectorMultiple=\"true\"\n class=\"fb-connector fb-connector--in\"\n title=\"Ingresso\"\n ></div>\n }\n\n <div class=\"fb-node__head\">\n <span class=\"fb-node__icon\" aria-hidden=\"true\">{{ node.icon }}</span>\n <div class=\"fb-node__text\">\n <span class=\"fb-node__title\" [title]=\"node.description || node.label\">{{ node.label }}</span>\n <span class=\"fb-node__sub\">\n {{ node.typeLabel }}\n @if (node.subtitle) {\n <span class=\"fb-node__sub-dot\">\u00B7</span>{{ node.subtitle }}\n }\n </span>\n </div>\n @if (node.hasAutomaticOutput) {\n <!-- L'elemento espone il proprio risultato sotto il proprio nome (\u00A74.5). -->\n <span class=\"fb-node__auto\" title=\"Espone un output automatico referenziabile come \u00AB{{ node.name }}\u00BB\">\n \u0192\n </span>\n }\n <!--\n `fDragBlocker` impedisce che il pointerdown sul bottone diventi un trascinamento\n del node: senza, aprire il dettaglio sposterebbe l'elemento di qualche pixel.\n -->\n <button\n type=\"button\"\n fDragBlocker\n class=\"fb-node__edit\"\n [attr.aria-label]=\"'Apri il dettaglio di ' + node.label\"\n title=\"Apri il dettaglio\"\n (click)=\"onEditClick($event, node.name)\"\n >\n \u270E\n </button>\n </div>\n\n <div class=\"fb-node__meta\">\n @if (!node.isStart) {\n <span class=\"fb-node__name\" [title]=\"'Nome tecnico: ' + node.name\">{{ node.name }}</span>\n }\n @if (!node.isReachable) {\n <span class=\"fb-badge fb-badge--unreachable\" title=\"Nessun percorso raggiunge questo elemento dallo Start\">\n scollegato\n </span>\n }\n @if (node.issueCount > 0) {\n <span\n class=\"fb-badge\"\n [class.fb-badge--error]=\"node.severity === 'Error'\"\n [class.fb-badge--warning]=\"node.severity === 'Warning'\"\n [class.fb-badge--info]=\"node.severity === 'Info'\"\n [title]=\"node.issueCount + ' rilievi di validazione'\"\n >\n {{ node.issueCount }}\n </span>\n }\n @if (node.danglingOutlets.length > 0) {\n <span\n class=\"fb-badge fb-badge--dangling\"\n [title]=\"'Rami dichiarati senza destinazione: ' + danglingLabels(node)\"\n >\n ramo incompleto\n </span>\n }\n </div>\n\n <!--\n Le uscite stanno sul bordo **inferiore**, una per ramo, nell'ordine in cui il modello\n le dichiara: per una Decision e' l'ordine di valutazione delle regole, che e'\n semantico (\u00A75.3), e da sinistra a destra si legge come la lista nell'inspector.\n L'etichetta si mostra solo quando i rami sono piu' di uno: su un `next` unico\n direbbe soltanto \u00ABSuccessivo\u00BB.\n -->\n <div class=\"fb-node__outlets\" [class.fb-node__outlets--labelled]=\"node.showsOutletLabels\">\n @for (outlet of node.outlets; track outlet.key) {\n <div class=\"fb-outlet\">\n @if (node.showsOutletLabels) {\n <span class=\"fb-outlet__label\" [title]=\"outlet.label\">{{ outlet.label }}</span>\n }\n <div\n fConnector\n fConnectorType=\"source\"\n [fConnectorId]=\"connectorIdOf(node, outlet.key)\"\n [fConnectorConnectableSide]=\"sides.BOTTOM\"\n [class]=\"'fb-connector fb-connector--out fb-connector--' + outlet.kind\"\n [title]=\"outlet.label\"\n ></div>\n </div>\n }\n </div>\n </div>\n }\n\n <!-- Anteprima dell'arco durante il trascinamento. -->\n <f-connection-for-create fBehavior=\"floating\" fType=\"bezier\" class=\"fb-edge fb-edge--creating\">\n <f-connection-marker-arrow [type]=\"markerEnd\" />\n </f-connection-for-create>\n\n <f-selection-area />\n </f-canvas>\n\n <f-minimap [fMinSize]=\"1200\" class=\"fb-minimap\" />\n</f-flow>\n", styles: [":host{display:block;position:relative;width:100%;height:100%;overflow:hidden;background:var(--fb-canvas-bg, #f4f5f7)}f-flow{display:block;width:100%;height:100%}.fb-minimap{position:absolute;right:14px;bottom:14px;width:190px;height:130px;border:1px solid var(--fb-border, #e2e5eb);border-radius:var(--fb-radius, 10px);background:var(--fb-surface, #fff);box-shadow:var(--fb-shadow-md, 0 6px 18px rgb(16 24 40 / 10%));overflow:hidden}.fb-node{position:absolute;display:flex;flex-direction:column;box-sizing:border-box;width:240px;padding:0;border:1px solid var(--fb-border, #e2e5eb);border-radius:var(--fb-radius-lg, 12px);background:var(--fb-surface, #fff);box-shadow:var(--fb-shadow-sm, 0 1px 2px rgb(16 24 40 / 6%));font:inherit;cursor:grab;-webkit-user-select:none;user-select:none;transition:box-shadow .12s ease,border-color .12s ease}.fb-node--outlets-3{width:304px}.fb-node--outlets-4{width:380px}.fb-node--outlets-5{width:456px}.fb-node--outlets-6{width:520px}.fb-node:hover{box-shadow:var(--fb-shadow-md, 0 6px 18px rgb(16 24 40 / 10%))}.fb-node--selected{border-color:var(--fb-accent, #4f6ef7);box-shadow:0 0 0 3px color-mix(in srgb,var(--fb-accent, #4f6ef7) 22%,transparent)}.fb-node--unreachable{border-style:dashed;opacity:.8}.fb-node--error{border-color:var(--fb-error, #c9372c)}.fb-node--warning{border-color:var(--fb-warning, #b7791f)}.fb-node__head{display:flex;align-items:center;gap:8px;padding:10px 10px 6px 12px}.fb-node__icon{display:grid;place-items:center;flex:0 0 auto;width:28px;height:28px;border-radius:var(--fb-radius-sm, 8px);background:var(--fb-node-accent, #667085);color:#fff;font-size:14px;line-height:1}.cat-start{--fb-node-accent: #22a06b}.cat-screen{--fb-node-accent: #3b82f6}.cat-logic{--fb-node-accent: #8b5cf6}.cat-data{--fb-node-accent: #06b6d4}.cat-action{--fb-node-accent: #f59e0b}.cat-flow{--fb-node-accent: #14b8a6}.cat-other{--fb-node-accent: #667085}.fb-node__text{flex:1;min-width:0}.fb-node__title{display:block;font-size:13px;font-weight:600;line-height:17px;color:var(--fb-text, #1a1c23);overflow:hidden;text-overflow:ellipsis;white-space:nowrap}.fb-node__sub{display:block;margin-top:1px;font-size:11px;line-height:14px;color:var(--fb-text-muted, #6b7086);overflow:hidden;text-overflow:ellipsis;white-space:nowrap}.fb-node__sub-dot{margin:0 4px;color:var(--fb-text-subtle, #98a2b3)}.fb-node__auto{flex:0 0 auto;font-size:12px;font-weight:700;color:var(--fb-accent, #4f6ef7);cursor:help}.fb-node__edit{display:grid;place-items:center;flex:0 0 auto;width:22px;height:22px;padding:0;border:0;border-radius:var(--fb-radius-xs, 6px);background:transparent;color:var(--fb-text-subtle, #98a2b3);font:inherit;font-size:12px;cursor:pointer;opacity:0;transition:opacity .12s ease,background .12s ease}.fb-node:hover .fb-node__edit,.fb-node--selected .fb-node__edit,.fb-node__edit:focus-visible{opacity:1}.fb-node__edit:hover{background:var(--fb-surface-alt, #f7f8fa);color:var(--fb-text, #1a1c23)}.fb-node__meta{display:flex;flex-wrap:wrap;align-items:center;gap:4px;min-height:14px;padding:0 12px 6px}.fb-node__name{flex:0 1 auto;font-family:ui-monospace,SFMono-Regular,Menlo,monospace;font-size:10px;color:var(--fb-text-subtle, #98a2b3);overflow:hidden;text-overflow:ellipsis;white-space:nowrap}.fb-badge{padding:1px 6px;border-radius:10px;background:var(--fb-badge-bg, #eef0f4);font-size:10px;font-weight:600;color:var(--fb-text-muted, #6b7086);white-space:nowrap;cursor:help}.fb-badge--error{background:color-mix(in srgb,var(--fb-error, #c9372c) 14%,transparent);color:var(--fb-error, #c9372c)}.fb-badge--warning{background:color-mix(in srgb,var(--fb-warning, #b7791f) 16%,transparent);color:var(--fb-warning, #b7791f)}.fb-badge--info{background:color-mix(in srgb,var(--fb-accent, #4f6ef7) 12%,transparent);color:var(--fb-accent, #4f6ef7)}.fb-badge--unreachable,.fb-badge--dangling{background:color-mix(in srgb,var(--fb-warning, #b7791f) 12%,transparent);color:var(--fb-warning, #b7791f)}.fb-node__outlets{display:flex;align-items:flex-end;justify-content:space-evenly;gap:4px;padding:0 8px 4px}.fb-node__outlets--labelled{padding-top:5px;border-top:1px solid var(--fb-border-subtle, #eef0f4)}.fb-outlet{display:flex;flex:1 1 0;flex-direction:column;align-items:center;gap:2px;min-width:0}.fb-outlet__label{max-width:100%;font-size:10px;line-height:13px;color:var(--fb-text-muted, #6b7086);text-align:center;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}.fb-connector{box-sizing:border-box;width:12px;height:12px;flex:0 0 auto;border:2px solid var(--fb-surface, #fff);border-radius:50%;background:var(--fb-connector, #98a2b3);cursor:crosshair;transition:transform .12s ease,box-shadow .12s ease}f-flow .fb-connector--out{position:relative;inset:auto;margin-bottom:-12px}.fb-connector--out:hover{transform:scale(1.2)}f-flow .fb-connector--in{position:absolute;inset:-2px auto auto 50%;width:24px;height:4px;border:0;border-radius:2px;transform:translate(-50%);background:var(--fb-border-strong, #cfd4de)}.fb-connector--out{--ff-connector-connected-color: var(--fb-connector, #98a2b3)}.fb-connector--Next,.fb-connector--Start,.fb-connector--LoopNext{--fb-connector: var(--fb-edge-next, #98a2b3)}.fb-connector--Rule,.fb-connector--WaitEvent{--fb-connector: var(--fb-edge-rule, #8b5cf6)}.fb-connector--Default,.fb-connector--LoopEnd{--fb-connector: var(--fb-edge-default, #06b6d4)}.fb-connector--Fault{--fb-connector: var(--fb-edge-fault, #dc2626)}.fb-connector--Timeout,.fb-connector--ScheduledPath{--fb-connector: var(--fb-edge-timeout, #f59e0b)}.fb-connector.f-connector-connectable{box-shadow:0 0 0 4px color-mix(in srgb,var(--fb-accent, #4f6ef7) 28%,transparent)}\n"] }]
2900
+ args: [{ selector: 'fb-flow-canvas', standalone: true, imports: [FFlowModule], changeDetection: ChangeDetectionStrategy.OnPush, template: "<!--\r\n Gerarchia obbligatoria: f-flow > f-canvas > fNode / f-connection.\r\n I `@for` sono direttamente dentro <f-canvas>: nessun wrapper, quindi non serve\r\n `ngProjectAs` (che sarebbe indispensabile con blocchi annidati).\r\n-->\r\n<f-flow\r\n fDraggable\r\n (fCreateConnection)=\"onCreateConnection($event)\"\r\n (fReassignConnection)=\"onReassignConnection($event)\"\r\n (fMoveNodes)=\"onMoveNodes($event)\"\r\n (fSelectionChange)=\"onSelectionChange($event)\"\r\n (fDeleteSelected)=\"onDeleteSelected($event)\"\r\n (fCreateNode)=\"onCreateNode($event)\"\r\n>\r\n <f-canvas fZoom>\r\n <f-background>\r\n <f-circle-pattern />\r\n </f-background>\r\n\r\n @for (edge of edges(); track edge.id) {\r\n <f-connection\r\n [fConnectionId]=\"edge.id\"\r\n [fSourceId]=\"edge.sourceId\"\r\n [fTargetId]=\"edge.targetId\"\r\n fBehavior=\"floating\"\r\n [fType]=\"edge.isGoTo ? 'segment' : 'bezier'\"\r\n [class]=\"'fb-edge fb-edge--' + edge.kind + (edge.isGoTo ? ' fb-edge--goto' : '')\"\r\n >\r\n <f-connection-marker-arrow [type]=\"markerEnd\" />\r\n @if (edge.label) {\r\n <div fConnectionContent class=\"fb-edge-label\">{{ edge.label }}</div>\r\n }\r\n </f-connection>\r\n }\r\n\r\n @for (node of nodes(); track node.id) {\r\n <div\r\n fNode\r\n fDragHandle\r\n [fNodeId]=\"node.id\"\r\n [fNodePosition]=\"node.position\"\r\n [class]=\"'fb-node ' + categoryClass(node) + ' ' + node.widthClass\"\r\n [class.fb-node--start]=\"node.isStart\"\r\n [class.fb-node--selected]=\"isSelected(node)\"\r\n [class.fb-node--unreachable]=\"!node.isReachable\"\r\n [class.fb-node--error]=\"node.severity === 'Error'\"\r\n [class.fb-node--warning]=\"node.severity === 'Warning'\"\r\n (dblclick)=\"onNodeDoubleClick(node.name)\"\r\n >\r\n <!--\r\n Lo Start non ha ingresso: e' il punto di partenza (\u00A73.4).\r\n `fConnectorConnectableSide` e' cio' che fa entrare l'arco dall'alto: senza, foblex\r\n calcola il lato e un arco che scende dal node sopra potrebbe agganciarsi di fianco.\r\n -->\r\n @if (!node.isStart) {\r\n <div\r\n fConnector\r\n fConnectorType=\"target\"\r\n [fConnectorId]=\"targetIdOf(node)\"\r\n [fConnectorConnectableSide]=\"sides.TOP\"\r\n fConnectorMultiple=\"true\"\r\n class=\"fb-connector fb-connector--in\"\r\n title=\"Ingresso\"\r\n ></div>\r\n }\r\n\r\n <div class=\"fb-node__head\">\r\n <span class=\"fb-node__icon\" aria-hidden=\"true\">{{ node.icon }}</span>\r\n <div class=\"fb-node__text\">\r\n <span class=\"fb-node__title\" [title]=\"node.description || node.label\">{{ node.label }}</span>\r\n <span class=\"fb-node__sub\">\r\n {{ node.typeLabel }}\r\n @if (node.subtitle) {\r\n <span class=\"fb-node__sub-dot\">\u00B7</span>{{ node.subtitle }}\r\n }\r\n </span>\r\n </div>\r\n @if (node.hasAutomaticOutput) {\r\n <!-- L'elemento espone il proprio risultato sotto il proprio nome (\u00A74.5). -->\r\n <span class=\"fb-node__auto\" title=\"Espone un output automatico referenziabile come \u00AB{{ node.name }}\u00BB\">\r\n \u0192\r\n </span>\r\n }\r\n <!--\r\n `fDragBlocker` impedisce che il pointerdown sul bottone diventi un trascinamento\r\n del node: senza, aprire il dettaglio sposterebbe l'elemento di qualche pixel.\r\n -->\r\n <button\r\n type=\"button\"\r\n fDragBlocker\r\n class=\"fb-node__edit\"\r\n [attr.aria-label]=\"'Apri il dettaglio di ' + node.label\"\r\n title=\"Apri il dettaglio\"\r\n (click)=\"onEditClick($event, node.name)\"\r\n >\r\n \u270E\r\n </button>\r\n </div>\r\n\r\n <div class=\"fb-node__meta\">\r\n @if (!node.isStart) {\r\n <span class=\"fb-node__name\" [title]=\"'Nome tecnico: ' + node.name\">{{ node.name }}</span>\r\n }\r\n @if (!node.isReachable) {\r\n <span class=\"fb-badge fb-badge--unreachable\" title=\"Nessun percorso raggiunge questo elemento dallo Start\">\r\n scollegato\r\n </span>\r\n }\r\n @if (node.issueCount > 0) {\r\n <span\r\n class=\"fb-badge\"\r\n [class.fb-badge--error]=\"node.severity === 'Error'\"\r\n [class.fb-badge--warning]=\"node.severity === 'Warning'\"\r\n [class.fb-badge--info]=\"node.severity === 'Info'\"\r\n [title]=\"node.issueCount + ' rilievi di validazione'\"\r\n >\r\n {{ node.issueCount }}\r\n </span>\r\n }\r\n @if (node.danglingOutlets.length > 0) {\r\n <span\r\n class=\"fb-badge fb-badge--dangling\"\r\n [title]=\"'Rami dichiarati senza destinazione: ' + danglingLabels(node)\"\r\n >\r\n ramo incompleto\r\n </span>\r\n }\r\n </div>\r\n\r\n <!--\r\n Le uscite stanno sul bordo **inferiore**, una per ramo, nell'ordine in cui il modello\r\n le dichiara: per una Decision e' l'ordine di valutazione delle regole, che e'\r\n semantico (\u00A75.3), e da sinistra a destra si legge come la lista nell'inspector.\r\n L'etichetta si mostra solo quando i rami sono piu' di uno: su un `next` unico\r\n direbbe soltanto \u00ABSuccessivo\u00BB.\r\n -->\r\n <div class=\"fb-node__outlets\" [class.fb-node__outlets--labelled]=\"node.showsOutletLabels\">\r\n @for (outlet of node.outlets; track outlet.key) {\r\n <div class=\"fb-outlet\">\r\n @if (node.showsOutletLabels) {\r\n <span class=\"fb-outlet__label\" [title]=\"outlet.label\">{{ outlet.label }}</span>\r\n }\r\n <div\r\n fConnector\r\n fConnectorType=\"source\"\r\n [fConnectorId]=\"connectorIdOf(node, outlet.key)\"\r\n [fConnectorConnectableSide]=\"sides.BOTTOM\"\r\n [class]=\"'fb-connector fb-connector--out fb-connector--' + outlet.kind\"\r\n [title]=\"outlet.label\"\r\n ></div>\r\n </div>\r\n }\r\n </div>\r\n </div>\r\n }\r\n\r\n <!-- Anteprima dell'arco durante il trascinamento. -->\r\n <f-connection-for-create fBehavior=\"floating\" fType=\"bezier\" class=\"fb-edge fb-edge--creating\">\r\n <f-connection-marker-arrow [type]=\"markerEnd\" />\r\n </f-connection-for-create>\r\n\r\n <f-selection-area />\r\n </f-canvas>\r\n\r\n <f-minimap [fMinSize]=\"1200\" class=\"fb-minimap\" />\r\n</f-flow>\r\n", styles: [":host{display:block;position:relative;width:100%;height:100%;overflow:hidden;background:var(--fb-canvas-bg, #f4f5f7)}f-flow{display:block;width:100%;height:100%}.fb-minimap{position:absolute;right:14px;bottom:14px;width:190px;height:130px;border:1px solid var(--fb-border, #e2e5eb);border-radius:var(--fb-radius, 10px);background:var(--fb-surface, #fff);box-shadow:var(--fb-shadow-md, 0 6px 18px rgb(16 24 40 / 10%));overflow:hidden}.fb-node{position:absolute;display:flex;flex-direction:column;box-sizing:border-box;width:240px;padding:0;border:1px solid var(--fb-border, #e2e5eb);border-radius:var(--fb-radius-lg, 12px);background:var(--fb-surface, #fff);box-shadow:var(--fb-shadow-sm, 0 1px 2px rgb(16 24 40 / 6%));font:inherit;cursor:grab;-webkit-user-select:none;user-select:none;transition:box-shadow .12s ease,border-color .12s ease}.fb-node--outlets-3{width:304px}.fb-node--outlets-4{width:380px}.fb-node--outlets-5{width:456px}.fb-node--outlets-6{width:520px}.fb-node:hover{box-shadow:var(--fb-shadow-md, 0 6px 18px rgb(16 24 40 / 10%))}.fb-node--selected{border-color:var(--fb-accent, #4f6ef7);box-shadow:0 0 0 3px color-mix(in srgb,var(--fb-accent, #4f6ef7) 22%,transparent)}.fb-node--unreachable{border-style:dashed;opacity:.8}.fb-node--error{border-color:var(--fb-error, #c9372c)}.fb-node--warning{border-color:var(--fb-warning, #b7791f)}.fb-node__head{display:flex;align-items:center;gap:8px;padding:10px 10px 6px 12px}.fb-node__icon{display:grid;place-items:center;flex:0 0 auto;width:28px;height:28px;border-radius:var(--fb-radius-sm, 8px);background:var(--fb-node-accent, #667085);color:#fff;font-size:14px;line-height:1}.cat-start{--fb-node-accent: #22a06b}.cat-screen{--fb-node-accent: #3b82f6}.cat-logic{--fb-node-accent: #8b5cf6}.cat-data{--fb-node-accent: #06b6d4}.cat-action{--fb-node-accent: #f59e0b}.cat-flow{--fb-node-accent: #14b8a6}.cat-other{--fb-node-accent: #667085}.fb-node__text{flex:1;min-width:0}.fb-node__title{display:block;font-size:13px;font-weight:600;line-height:17px;color:var(--fb-text, #1a1c23);overflow:hidden;text-overflow:ellipsis;white-space:nowrap}.fb-node__sub{display:block;margin-top:1px;font-size:11px;line-height:14px;color:var(--fb-text-muted, #6b7086);overflow:hidden;text-overflow:ellipsis;white-space:nowrap}.fb-node__sub-dot{margin:0 4px;color:var(--fb-text-subtle, #98a2b3)}.fb-node__auto{flex:0 0 auto;font-size:12px;font-weight:700;color:var(--fb-accent, #4f6ef7);cursor:help}.fb-node__edit{display:grid;place-items:center;flex:0 0 auto;width:22px;height:22px;padding:0;border:0;border-radius:var(--fb-radius-xs, 6px);background:transparent;color:var(--fb-text-subtle, #98a2b3);font:inherit;font-size:12px;cursor:pointer;opacity:0;transition:opacity .12s ease,background .12s ease}.fb-node:hover .fb-node__edit,.fb-node--selected .fb-node__edit,.fb-node__edit:focus-visible{opacity:1}.fb-node__edit:hover{background:var(--fb-surface-alt, #f7f8fa);color:var(--fb-text, #1a1c23)}.fb-node__meta{display:flex;flex-wrap:wrap;align-items:center;gap:4px;min-height:14px;padding:0 12px 6px}.fb-node__name{flex:0 1 auto;font-family:ui-monospace,SFMono-Regular,Menlo,monospace;font-size:10px;color:var(--fb-text-subtle, #98a2b3);overflow:hidden;text-overflow:ellipsis;white-space:nowrap}.fb-badge{padding:1px 6px;border-radius:10px;background:var(--fb-badge-bg, #eef0f4);font-size:10px;font-weight:600;color:var(--fb-text-muted, #6b7086);white-space:nowrap;cursor:help}.fb-badge--error{background:color-mix(in srgb,var(--fb-error, #c9372c) 14%,transparent);color:var(--fb-error, #c9372c)}.fb-badge--warning{background:color-mix(in srgb,var(--fb-warning, #b7791f) 16%,transparent);color:var(--fb-warning, #b7791f)}.fb-badge--info{background:color-mix(in srgb,var(--fb-accent, #4f6ef7) 12%,transparent);color:var(--fb-accent, #4f6ef7)}.fb-badge--unreachable,.fb-badge--dangling{background:color-mix(in srgb,var(--fb-warning, #b7791f) 12%,transparent);color:var(--fb-warning, #b7791f)}.fb-node__outlets{display:flex;align-items:flex-end;justify-content:space-evenly;gap:4px;padding:0 8px 4px}.fb-node__outlets--labelled{padding-top:5px;border-top:1px solid var(--fb-border-subtle, #eef0f4)}.fb-outlet{display:flex;flex:1 1 0;flex-direction:column;align-items:center;gap:2px;min-width:0}.fb-outlet__label{max-width:100%;font-size:10px;line-height:13px;color:var(--fb-text-muted, #6b7086);text-align:center;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}.fb-connector{box-sizing:border-box;width:12px;height:12px;flex:0 0 auto;border:2px solid var(--fb-surface, #fff);border-radius:50%;background:var(--fb-connector, #98a2b3);cursor:crosshair;transition:transform .12s ease,box-shadow .12s ease}f-flow .fb-connector--out{position:relative;inset:auto;margin-bottom:-12px}.fb-connector--out:hover{transform:scale(1.2)}f-flow .fb-connector--in{position:absolute;inset:-2px auto auto 50%;width:24px;height:4px;border:0;border-radius:2px;transform:translate(-50%);background:var(--fb-border-strong, #cfd4de)}.fb-connector--out{--ff-connector-connected-color: var(--fb-connector, #98a2b3)}.fb-connector--Next,.fb-connector--Start,.fb-connector--LoopNext{--fb-connector: var(--fb-edge-next, #98a2b3)}.fb-connector--Rule,.fb-connector--WaitEvent{--fb-connector: var(--fb-edge-rule, #8b5cf6)}.fb-connector--Default,.fb-connector--LoopEnd{--fb-connector: var(--fb-edge-default, #06b6d4)}.fb-connector--Fault{--fb-connector: var(--fb-edge-fault, #dc2626)}.fb-connector--Timeout,.fb-connector--ScheduledPath{--fb-connector: var(--fb-edge-timeout, #f59e0b)}.fb-connector.f-connector-connectable{box-shadow:0 0 0 4px color-mix(in srgb,var(--fb-accent, #4f6ef7) 28%,transparent)}\n"] }]
2667
2901
  }], propDecorators: { selectedName: [{ type: i0.Input, args: [{ isSignal: true, alias: "selectedName", required: false }] }], outline: [{ type: i0.Input, args: [{ isSignal: true, alias: "outline", required: false }] }], selectionChange: [{ type: i0.Output, args: ["selectionChange"] }], nodeOpened: [{ type: i0.Output, args: ["nodeOpened"] }], nodeRemoveRequested: [{ type: i0.Output, args: ["nodeRemoveRequested"] }], nodeDuplicateRequested: [{ type: i0.Output, args: ["nodeDuplicateRequested"] }], elementDropped: [{ type: i0.Output, args: ["elementDropped"] }] } });
2668
2902
 
2669
2903
  /**
@@ -2724,6 +2958,18 @@ class ElementPaletteComponent {
2724
2958
  iconOf(entry) {
2725
2959
  return elementIcon(entry.value, entry.label);
2726
2960
  }
2961
+ /**
2962
+ * L'etichetta del ramo di fault, presa dalla mappa delle uscite invece di scrivere «ramo di
2963
+ * errore» per tutti: su uno stage di orchestrazione quel ramo **non** e' un guasto, e' lo step
2964
+ * rifiutato (§5.13). Un tipo nuovo eredita l'etichetta giusta senza toccare la palette.
2965
+ */
2966
+ faultLabel(entry) {
2967
+ if (!entry.hasFaultConnector) {
2968
+ return null;
2969
+ }
2970
+ const outlet = outletsOf(entry.value, {}).find((candidate) => candidate.kind === 'Fault');
2971
+ return outlet ? `ramo «${outlet.label.toLowerCase()}»` : 'ramo di errore';
2972
+ }
2727
2973
  categoryClass(category) {
2728
2974
  const normalized = category.toLowerCase();
2729
2975
  if (normalized.startsWith('inter')) {
@@ -2744,11 +2990,11 @@ class ElementPaletteComponent {
2744
2990
  return 'cat-other';
2745
2991
  }
2746
2992
  static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.3.27", ngImport: i0, type: ElementPaletteComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
2747
- static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "20.3.27", type: ElementPaletteComponent, isStandalone: true, selector: "fb-element-palette", inputs: { processType: { classPropertyName: "processType", publicName: "processType", isSignal: true, isRequired: false, transformFunction: null } }, outputs: { elementPicked: "elementPicked" }, ngImport: i0, template: "<div class=\"fb-palette__search\">\n <input\n type=\"search\"\n placeholder=\"Cerca un elemento\"\n aria-label=\"Cerca un elemento\"\n (input)=\"onFilter($any($event.target).value)\"\n />\n</div>\n\n@if (isEmpty()) {\n <p class=\"fb-palette__empty\">\n Il dizionario degli elementi non e\u2019 ancora disponibile.\n </p>\n}\n\n<div class=\"fb-palette__groups\">\n @for (group of groups(); track group.category) {\n <section class=\"fb-palette__group\">\n <h3 class=\"fb-palette__category\">{{ group.category }}</h3>\n @for (entry of group.entries; track entry.value) {\n <!--\n `fExternalItem` rende la voce trascinabile sul canvas: il rilascio emette\n `fCreateNode` con questo `fData` e la posizione, che diventa locationX/locationY.\n -->\n <button\n type=\"button\"\n class=\"fb-palette__item\"\n fExternalItem\n [fExternalItemId]=\"entry.value\"\n [fData]=\"entry.value\"\n [class.fb-palette__item--warn]=\"isIncompatible(entry)\"\n [title]=\"incompatibleHint(entry) || entry.description || entry.label\"\n (click)=\"pick(entry)\"\n >\n <span class=\"fb-palette__icon\" [class]=\"'fb-palette__icon ' + categoryClass(group.category)\">\n {{ iconOf(entry) }}\n </span>\n <span class=\"fb-palette__text\">\n <span class=\"fb-palette__label\">{{ entry.label }}</span>\n @if (entry.hasAutomaticOutput) {\n <span class=\"fb-palette__flag\" title=\"Espone un output automatico sotto il nome dell\u2019elemento\">\n output automatico\n </span>\n }\n @if (entry.hasFaultConnector) {\n <span class=\"fb-palette__flag\" title=\"Ha un ramo di errore che l\u2019autore puo\u2019 prevedere\">\n ramo di errore\n </span>\n }\n </span>\n @if (isIncompatible(entry)) {\n <span class=\"fb-palette__warn\" aria-hidden=\"true\">!</span>\n }\n </button>\n }\n </section>\n }\n</div>\n", styles: [":host{display:flex;flex-direction:column;height:100%;min-height:0;background:var(--fb-surface, #fff);border-right:1px solid var(--fb-border, #d6dae1)}.fb-palette__search{padding:8px;border-bottom:1px solid var(--fb-border-subtle, #e6e9ee)}.fb-palette__search input{box-sizing:border-box;width:100%;padding:5px 8px;border:1px solid var(--fb-border, #d6dae1);border-radius:6px;background:var(--fb-surface, #fff);color:var(--fb-text, #1d2939);font:inherit;font-size:12px}.fb-palette__search input:focus-visible{outline:2px solid var(--fb-accent, #2f6feb);outline-offset:-1px}.fb-palette__groups{flex:1;min-height:0;overflow-y:auto;padding:4px 0 12px}.fb-palette__empty{margin:12px 10px;font-size:12px;color:var(--fb-text-muted, #667085)}.fb-palette__category{margin:10px 10px 4px;font-size:10px;font-weight:700;letter-spacing:.06em;text-transform:uppercase;color:var(--fb-text-subtle, #98a2b3)}.fb-palette__item{display:flex;align-items:center;gap:8px;box-sizing:border-box;width:calc(100% - 12px);margin:1px 6px;padding:5px 8px;border:0;border-radius:var(--fb-radius-xs, 6px);background:transparent;color:var(--fb-text, #1a1c23);font:inherit;text-align:left;cursor:grab;transition:background .12s ease}.fb-palette__item:hover{background:var(--fb-surface-alt, #f7f8fa)}.fb-palette__item:focus-visible{outline:2px solid var(--fb-accent, #2f6feb);outline-offset:-2px}.fb-palette__icon{display:grid;place-items:center;flex:0 0 auto;width:24px;height:24px;border-radius:var(--fb-radius-xs, 6px);background:var(--fb-icon-bg, #98a2b3);color:#fff;font-size:11px;font-weight:700}.cat-screen{--fb-icon-bg: #3b82f6}.cat-logic{--fb-icon-bg: #8b5cf6}.cat-data{--fb-icon-bg: #06b6d4}.cat-action{--fb-icon-bg: #f59e0b}.cat-flow{--fb-icon-bg: #14b8a6}.fb-palette__text{display:flex;flex-direction:column;min-width:0}.fb-palette__label{font-size:12px;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}.fb-palette__flag{font-size:9px;color:var(--fb-text-subtle, #98a2b3)}.fb-palette__item--warn .fb-palette__label{color:var(--fb-warning, #b7791f)}.fb-palette__warn{margin-left:auto;color:var(--fb-warning, #b7791f);font-weight:700}:host ::ng-deep .f-external-item-preview{padding:4px 8px;border:1px solid var(--fb-accent, #2f6feb);border-radius:6px;background:var(--fb-surface, #fff);box-shadow:0 4px 12px #1018282e;opacity:.95}\n"], dependencies: [{ kind: "ngmodule", type: FFlowModule }, { kind: "directive", type: i1.FExternalItem, selector: "[fExternalItem]", inputs: ["fExternalItemId", "fData", "fDisabled", "fPreview", "fPreviewMatchSize", "fPlaceholder"], outputs: ["fPreviewChange", "fPlaceholderChange"] }], changeDetection: i0.ChangeDetectionStrategy.OnPush });
2993
+ static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "20.3.27", type: ElementPaletteComponent, isStandalone: true, selector: "fb-element-palette", inputs: { processType: { classPropertyName: "processType", publicName: "processType", isSignal: true, isRequired: false, transformFunction: null } }, outputs: { elementPicked: "elementPicked" }, ngImport: i0, template: "<div class=\"fb-palette__search\">\r\n <input\r\n type=\"search\"\r\n placeholder=\"Cerca un elemento\"\r\n aria-label=\"Cerca un elemento\"\r\n (input)=\"onFilter($any($event.target).value)\"\r\n />\r\n</div>\r\n\r\n@if (isEmpty()) {\r\n <p class=\"fb-palette__empty\">\r\n Il dizionario degli elementi non e\u2019 ancora disponibile.\r\n </p>\r\n}\r\n\r\n<div class=\"fb-palette__groups\">\r\n @for (group of groups(); track group.category) {\r\n <section class=\"fb-palette__group\">\r\n <h3 class=\"fb-palette__category\">{{ group.category }}</h3>\r\n @for (entry of group.entries; track entry.value) {\r\n <!--\r\n `fExternalItem` rende la voce trascinabile sul canvas: il rilascio emette\r\n `fCreateNode` con questo `fData` e la posizione, che diventa locationX/locationY.\r\n -->\r\n <button\r\n type=\"button\"\r\n class=\"fb-palette__item\"\r\n fExternalItem\r\n [fExternalItemId]=\"entry.value\"\r\n [fData]=\"entry.value\"\r\n [class.fb-palette__item--warn]=\"isIncompatible(entry)\"\r\n [title]=\"incompatibleHint(entry) || entry.description || entry.label\"\r\n (click)=\"pick(entry)\"\r\n >\r\n <span class=\"fb-palette__icon\" [class]=\"'fb-palette__icon ' + categoryClass(group.category)\">\r\n {{ iconOf(entry) }}\r\n </span>\r\n <span class=\"fb-palette__text\">\r\n <span class=\"fb-palette__label\">{{ entry.label }}</span>\r\n @if (entry.hasAutomaticOutput) {\r\n <span class=\"fb-palette__flag\" title=\"Espone un output automatico sotto il nome dell\u2019elemento\">\r\n output automatico\r\n </span>\r\n }\r\n @if (faultLabel(entry)) {\r\n <span class=\"fb-palette__flag\" title=\"Ha un ramo che l\u2019autore puo\u2019 prevedere e disegnare\">\r\n {{ faultLabel(entry) }}\r\n </span>\r\n }\r\n </span>\r\n @if (isIncompatible(entry)) {\r\n <span class=\"fb-palette__warn\" aria-hidden=\"true\">!</span>\r\n }\r\n </button>\r\n }\r\n </section>\r\n }\r\n</div>\r\n", styles: [":host{display:flex;flex-direction:column;height:100%;min-height:0;background:var(--fb-surface, #fff);border-right:1px solid var(--fb-border, #d6dae1)}.fb-palette__search{padding:8px;border-bottom:1px solid var(--fb-border-subtle, #e6e9ee)}.fb-palette__search input{box-sizing:border-box;width:100%;padding:5px 8px;border:1px solid var(--fb-border, #d6dae1);border-radius:6px;background:var(--fb-surface, #fff);color:var(--fb-text, #1d2939);font:inherit;font-size:12px}.fb-palette__search input:focus-visible{outline:2px solid var(--fb-accent, #2f6feb);outline-offset:-1px}.fb-palette__groups{flex:1;min-height:0;overflow-y:auto;padding:4px 0 12px}.fb-palette__empty{margin:12px 10px;font-size:12px;color:var(--fb-text-muted, #667085)}.fb-palette__category{margin:10px 10px 4px;font-size:10px;font-weight:700;letter-spacing:.06em;text-transform:uppercase;color:var(--fb-text-subtle, #98a2b3)}.fb-palette__item{display:flex;align-items:center;gap:8px;box-sizing:border-box;width:calc(100% - 12px);margin:1px 6px;padding:5px 8px;border:0;border-radius:var(--fb-radius-xs, 6px);background:transparent;color:var(--fb-text, #1a1c23);font:inherit;text-align:left;cursor:grab;transition:background .12s ease}.fb-palette__item:hover{background:var(--fb-surface-alt, #f7f8fa)}.fb-palette__item:focus-visible{outline:2px solid var(--fb-accent, #2f6feb);outline-offset:-2px}.fb-palette__icon{display:grid;place-items:center;flex:0 0 auto;width:24px;height:24px;border-radius:var(--fb-radius-xs, 6px);background:var(--fb-icon-bg, #98a2b3);color:#fff;font-size:11px;font-weight:700}.cat-screen{--fb-icon-bg: #3b82f6}.cat-logic{--fb-icon-bg: #8b5cf6}.cat-data{--fb-icon-bg: #06b6d4}.cat-action{--fb-icon-bg: #f59e0b}.cat-flow{--fb-icon-bg: #14b8a6}.fb-palette__text{display:flex;flex-direction:column;min-width:0}.fb-palette__label{font-size:12px;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}.fb-palette__flag{font-size:9px;color:var(--fb-text-subtle, #98a2b3)}.fb-palette__item--warn .fb-palette__label{color:var(--fb-warning, #b7791f)}.fb-palette__warn{margin-left:auto;color:var(--fb-warning, #b7791f);font-weight:700}:host ::ng-deep .f-external-item-preview{padding:4px 8px;border:1px solid var(--fb-accent, #2f6feb);border-radius:6px;background:var(--fb-surface, #fff);box-shadow:0 4px 12px #1018282e;opacity:.95}\n"], dependencies: [{ kind: "ngmodule", type: FFlowModule }, { kind: "directive", type: i1.FExternalItem, selector: "[fExternalItem]", inputs: ["fExternalItemId", "fData", "fDisabled", "fPreview", "fPreviewMatchSize", "fPlaceholder"], outputs: ["fPreviewChange", "fPlaceholderChange"] }], changeDetection: i0.ChangeDetectionStrategy.OnPush });
2748
2994
  }
2749
2995
  i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.27", ngImport: i0, type: ElementPaletteComponent, decorators: [{
2750
2996
  type: Component,
2751
- args: [{ selector: 'fb-element-palette', standalone: true, imports: [FFlowModule], changeDetection: ChangeDetectionStrategy.OnPush, template: "<div class=\"fb-palette__search\">\n <input\n type=\"search\"\n placeholder=\"Cerca un elemento\"\n aria-label=\"Cerca un elemento\"\n (input)=\"onFilter($any($event.target).value)\"\n />\n</div>\n\n@if (isEmpty()) {\n <p class=\"fb-palette__empty\">\n Il dizionario degli elementi non e\u2019 ancora disponibile.\n </p>\n}\n\n<div class=\"fb-palette__groups\">\n @for (group of groups(); track group.category) {\n <section class=\"fb-palette__group\">\n <h3 class=\"fb-palette__category\">{{ group.category }}</h3>\n @for (entry of group.entries; track entry.value) {\n <!--\n `fExternalItem` rende la voce trascinabile sul canvas: il rilascio emette\n `fCreateNode` con questo `fData` e la posizione, che diventa locationX/locationY.\n -->\n <button\n type=\"button\"\n class=\"fb-palette__item\"\n fExternalItem\n [fExternalItemId]=\"entry.value\"\n [fData]=\"entry.value\"\n [class.fb-palette__item--warn]=\"isIncompatible(entry)\"\n [title]=\"incompatibleHint(entry) || entry.description || entry.label\"\n (click)=\"pick(entry)\"\n >\n <span class=\"fb-palette__icon\" [class]=\"'fb-palette__icon ' + categoryClass(group.category)\">\n {{ iconOf(entry) }}\n </span>\n <span class=\"fb-palette__text\">\n <span class=\"fb-palette__label\">{{ entry.label }}</span>\n @if (entry.hasAutomaticOutput) {\n <span class=\"fb-palette__flag\" title=\"Espone un output automatico sotto il nome dell\u2019elemento\">\n output automatico\n </span>\n }\n @if (entry.hasFaultConnector) {\n <span class=\"fb-palette__flag\" title=\"Ha un ramo di errore che l\u2019autore puo\u2019 prevedere\">\n ramo di errore\n </span>\n }\n </span>\n @if (isIncompatible(entry)) {\n <span class=\"fb-palette__warn\" aria-hidden=\"true\">!</span>\n }\n </button>\n }\n </section>\n }\n</div>\n", styles: [":host{display:flex;flex-direction:column;height:100%;min-height:0;background:var(--fb-surface, #fff);border-right:1px solid var(--fb-border, #d6dae1)}.fb-palette__search{padding:8px;border-bottom:1px solid var(--fb-border-subtle, #e6e9ee)}.fb-palette__search input{box-sizing:border-box;width:100%;padding:5px 8px;border:1px solid var(--fb-border, #d6dae1);border-radius:6px;background:var(--fb-surface, #fff);color:var(--fb-text, #1d2939);font:inherit;font-size:12px}.fb-palette__search input:focus-visible{outline:2px solid var(--fb-accent, #2f6feb);outline-offset:-1px}.fb-palette__groups{flex:1;min-height:0;overflow-y:auto;padding:4px 0 12px}.fb-palette__empty{margin:12px 10px;font-size:12px;color:var(--fb-text-muted, #667085)}.fb-palette__category{margin:10px 10px 4px;font-size:10px;font-weight:700;letter-spacing:.06em;text-transform:uppercase;color:var(--fb-text-subtle, #98a2b3)}.fb-palette__item{display:flex;align-items:center;gap:8px;box-sizing:border-box;width:calc(100% - 12px);margin:1px 6px;padding:5px 8px;border:0;border-radius:var(--fb-radius-xs, 6px);background:transparent;color:var(--fb-text, #1a1c23);font:inherit;text-align:left;cursor:grab;transition:background .12s ease}.fb-palette__item:hover{background:var(--fb-surface-alt, #f7f8fa)}.fb-palette__item:focus-visible{outline:2px solid var(--fb-accent, #2f6feb);outline-offset:-2px}.fb-palette__icon{display:grid;place-items:center;flex:0 0 auto;width:24px;height:24px;border-radius:var(--fb-radius-xs, 6px);background:var(--fb-icon-bg, #98a2b3);color:#fff;font-size:11px;font-weight:700}.cat-screen{--fb-icon-bg: #3b82f6}.cat-logic{--fb-icon-bg: #8b5cf6}.cat-data{--fb-icon-bg: #06b6d4}.cat-action{--fb-icon-bg: #f59e0b}.cat-flow{--fb-icon-bg: #14b8a6}.fb-palette__text{display:flex;flex-direction:column;min-width:0}.fb-palette__label{font-size:12px;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}.fb-palette__flag{font-size:9px;color:var(--fb-text-subtle, #98a2b3)}.fb-palette__item--warn .fb-palette__label{color:var(--fb-warning, #b7791f)}.fb-palette__warn{margin-left:auto;color:var(--fb-warning, #b7791f);font-weight:700}:host ::ng-deep .f-external-item-preview{padding:4px 8px;border:1px solid var(--fb-accent, #2f6feb);border-radius:6px;background:var(--fb-surface, #fff);box-shadow:0 4px 12px #1018282e;opacity:.95}\n"] }]
2997
+ args: [{ selector: 'fb-element-palette', standalone: true, imports: [FFlowModule], changeDetection: ChangeDetectionStrategy.OnPush, template: "<div class=\"fb-palette__search\">\r\n <input\r\n type=\"search\"\r\n placeholder=\"Cerca un elemento\"\r\n aria-label=\"Cerca un elemento\"\r\n (input)=\"onFilter($any($event.target).value)\"\r\n />\r\n</div>\r\n\r\n@if (isEmpty()) {\r\n <p class=\"fb-palette__empty\">\r\n Il dizionario degli elementi non e\u2019 ancora disponibile.\r\n </p>\r\n}\r\n\r\n<div class=\"fb-palette__groups\">\r\n @for (group of groups(); track group.category) {\r\n <section class=\"fb-palette__group\">\r\n <h3 class=\"fb-palette__category\">{{ group.category }}</h3>\r\n @for (entry of group.entries; track entry.value) {\r\n <!--\r\n `fExternalItem` rende la voce trascinabile sul canvas: il rilascio emette\r\n `fCreateNode` con questo `fData` e la posizione, che diventa locationX/locationY.\r\n -->\r\n <button\r\n type=\"button\"\r\n class=\"fb-palette__item\"\r\n fExternalItem\r\n [fExternalItemId]=\"entry.value\"\r\n [fData]=\"entry.value\"\r\n [class.fb-palette__item--warn]=\"isIncompatible(entry)\"\r\n [title]=\"incompatibleHint(entry) || entry.description || entry.label\"\r\n (click)=\"pick(entry)\"\r\n >\r\n <span class=\"fb-palette__icon\" [class]=\"'fb-palette__icon ' + categoryClass(group.category)\">\r\n {{ iconOf(entry) }}\r\n </span>\r\n <span class=\"fb-palette__text\">\r\n <span class=\"fb-palette__label\">{{ entry.label }}</span>\r\n @if (entry.hasAutomaticOutput) {\r\n <span class=\"fb-palette__flag\" title=\"Espone un output automatico sotto il nome dell\u2019elemento\">\r\n output automatico\r\n </span>\r\n }\r\n @if (faultLabel(entry)) {\r\n <span class=\"fb-palette__flag\" title=\"Ha un ramo che l\u2019autore puo\u2019 prevedere e disegnare\">\r\n {{ faultLabel(entry) }}\r\n </span>\r\n }\r\n </span>\r\n @if (isIncompatible(entry)) {\r\n <span class=\"fb-palette__warn\" aria-hidden=\"true\">!</span>\r\n }\r\n </button>\r\n }\r\n </section>\r\n }\r\n</div>\r\n", styles: [":host{display:flex;flex-direction:column;height:100%;min-height:0;background:var(--fb-surface, #fff);border-right:1px solid var(--fb-border, #d6dae1)}.fb-palette__search{padding:8px;border-bottom:1px solid var(--fb-border-subtle, #e6e9ee)}.fb-palette__search input{box-sizing:border-box;width:100%;padding:5px 8px;border:1px solid var(--fb-border, #d6dae1);border-radius:6px;background:var(--fb-surface, #fff);color:var(--fb-text, #1d2939);font:inherit;font-size:12px}.fb-palette__search input:focus-visible{outline:2px solid var(--fb-accent, #2f6feb);outline-offset:-1px}.fb-palette__groups{flex:1;min-height:0;overflow-y:auto;padding:4px 0 12px}.fb-palette__empty{margin:12px 10px;font-size:12px;color:var(--fb-text-muted, #667085)}.fb-palette__category{margin:10px 10px 4px;font-size:10px;font-weight:700;letter-spacing:.06em;text-transform:uppercase;color:var(--fb-text-subtle, #98a2b3)}.fb-palette__item{display:flex;align-items:center;gap:8px;box-sizing:border-box;width:calc(100% - 12px);margin:1px 6px;padding:5px 8px;border:0;border-radius:var(--fb-radius-xs, 6px);background:transparent;color:var(--fb-text, #1a1c23);font:inherit;text-align:left;cursor:grab;transition:background .12s ease}.fb-palette__item:hover{background:var(--fb-surface-alt, #f7f8fa)}.fb-palette__item:focus-visible{outline:2px solid var(--fb-accent, #2f6feb);outline-offset:-2px}.fb-palette__icon{display:grid;place-items:center;flex:0 0 auto;width:24px;height:24px;border-radius:var(--fb-radius-xs, 6px);background:var(--fb-icon-bg, #98a2b3);color:#fff;font-size:11px;font-weight:700}.cat-screen{--fb-icon-bg: #3b82f6}.cat-logic{--fb-icon-bg: #8b5cf6}.cat-data{--fb-icon-bg: #06b6d4}.cat-action{--fb-icon-bg: #f59e0b}.cat-flow{--fb-icon-bg: #14b8a6}.fb-palette__text{display:flex;flex-direction:column;min-width:0}.fb-palette__label{font-size:12px;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}.fb-palette__flag{font-size:9px;color:var(--fb-text-subtle, #98a2b3)}.fb-palette__item--warn .fb-palette__label{color:var(--fb-warning, #b7791f)}.fb-palette__warn{margin-left:auto;color:var(--fb-warning, #b7791f);font-weight:700}:host ::ng-deep .f-external-item-preview{padding:4px 8px;border:1px solid var(--fb-accent, #2f6feb);border-radius:6px;background:var(--fb-surface, #fff);box-shadow:0 4px 12px #1018282e;opacity:.95}\n"] }]
2752
2998
  }], propDecorators: { processType: [{ type: i0.Input, args: [{ isSignal: true, alias: "processType", required: false }] }], elementPicked: [{ type: i0.Output, args: ["elementPicked"] }] } });
2753
2999
 
2754
3000
  /**
@@ -2763,8 +3009,11 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.27", ngImpo
2763
3009
  * pur non essendo dichiarati in nessuna collection di risorse: la primitiva li restituisce
2764
3010
  * già, ed e' il motivo per cui non si ricostruisce l'elenco lato client.
2765
3011
  *
2766
- * Resta possibile scrivere a mano: i percorsi di relazione e i campi di un record non
2767
- * sono enumerabili, e gli scope dell'host (`$User.X`) non sono verificabili affatto.
3012
+ * Resta possibile scrivere a mano: i percorsi di relazione e i campi di un record non sono
3013
+ * enumerabili. Gli scope del sistema ospite (`$User.X`), invece, sono verificabili **quando
3014
+ * l'host li dichiara**: se `globalVariables` porta almeno un percorso di quello scope, un
3015
+ * percorso che non c'e' e' `GLOBAL_UNKNOWN`; se non ne porta nessuno, lo scope resta non
3016
+ * verificabile — che significa "non lo so", non "non esiste" (§4.1).
2768
3017
  */
2769
3018
  class ReferencePickerComponent {
2770
3019
  api = inject(FlowBuilderApi);
@@ -2783,11 +3032,6 @@ class ReferencePickerComponent {
2783
3032
  * formula e' `TARGET_NOT_WRITABLE` (§4.6).
2784
3033
  */
2785
3034
  writableOnly = input(false, ...(ngDevMode ? [{ debugName: "writableOnly" }] : []));
2786
- /**
2787
- * `true` → aggiunge `$Flow.CurrentStage` e `$Flow.ActiveStages` alle destinazioni.
2788
- * Sono le sole variabili globali scrivibili, e solo se il flow dichiara stage (§5.2).
2789
- */
2790
- allowStageTargets = input(false, ...(ngDevMode ? [{ debugName: "allowStageTargets" }] : []));
2791
3035
  /**
2792
3036
  * `true` → a sinistra ci va il nome di un **node**, non di una risorsa: e' il caso di
2793
3037
  * `WasVisited` e `HasError` (§4.3).
@@ -2819,7 +3063,18 @@ class ReferencePickerComponent {
2819
3063
  })));
2820
3064
  return;
2821
3065
  }
2822
- const query = { definition, dataType, isCollection, objectType };
3066
+ /**
3067
+ * `?? undefined` non e' cosmetico: nei template il safe navigation di Angular
3068
+ * (`describe(x)?.isCollection`) produce **null**, non `undefined`. Un `isCollection: null`
3069
+ * che arriva alla primitiva viene letto come "filtra per valore singolo" e svuota
3070
+ * l'elenco: un campo senza filtro deve mandare il campo **assente**.
3071
+ */
3072
+ const query = {
3073
+ definition,
3074
+ dataType: dataType ?? undefined,
3075
+ isCollection: isCollection ?? undefined,
3076
+ objectType: objectType ?? undefined,
3077
+ };
2823
3078
  this.isLoading.set(true);
2824
3079
  this.loadError.set(null);
2825
3080
  const request = writable ? this.api.getWritableReferences(query) : this.api.getReferences(query);
@@ -2833,30 +3088,12 @@ class ReferencePickerComponent {
2833
3088
  .finally(() => this.isLoading.set(false));
2834
3089
  });
2835
3090
  }
2836
- /** Le due globali di stage, aggiunte alle destinazioni quando il flow dichiara stage. */
2837
- stageTargets = computed(() => {
2838
- if (!this.allowStageTargets() || !(this.store.document().stages?.length ?? 0)) {
2839
- return [];
2840
- }
2841
- return [
2842
- {
2843
- name: '$Flow.CurrentStage',
2844
- kind: 'Global',
2845
- isWritable: true,
2846
- description: 'Lo stage corrente: si fa avanzare con un Assignment.',
2847
- },
2848
- {
2849
- name: '$Flow.ActiveStages',
2850
- kind: 'Global',
2851
- isCollection: true,
2852
- isWritable: true,
2853
- description: 'La collection degli stage attivi.',
2854
- },
2855
- ];
2856
- }, ...(ngDevMode ? [{ debugName: "stageTargets" }] : []));
2857
3091
  options = computed(() => {
2858
3092
  const needle = this.query().trim().toLowerCase();
2859
- const all = [...this.stageTargets(), ...this.references()];
3093
+ // Le globali assegnabili — `$Flow.CurrentStage`, `$Flow.ActiveStages` e quelle che l'host
3094
+ // dichiara scrivibili — le comprende già `POST /flows/references/writable`: aggiungerle
3095
+ // qui le duplicherebbe (§5.2, §6.4).
3096
+ const all = this.references();
2860
3097
  if (!needle) {
2861
3098
  return all;
2862
3099
  }
@@ -2908,35 +3145,34 @@ class ReferencePickerComponent {
2908
3145
  * - `navigated`: la radice e' nota ma c'e' un percorso dopo il punto (campo di record,
2909
3146
  * output automatico navigato): la validazione non verifica i percorsi con il punto,
2910
3147
  * quindi si accetta senza segnalare;
2911
- * - `host`: scope dell'host, non verificabile per costruzione (§4.1);
2912
- * - `unknown`: nessuna corrispondenza: si avvisa, senza bloccare.
3148
+ * - `host`: scope del sistema ospite che **non dichiara percorsi**: non verificabile (§4.1);
3149
+ * - `unknown`: nessuna corrispondenza: si avvisa, senza bloccare. Ci finisce anche il
3150
+ * percorso inesistente di uno scope che i percorsi li dichiara, perche' lì il backend
3151
+ * risponderebbe `GLOBAL_UNKNOWN`.
2913
3152
  */
2914
3153
  valueState = computed(() => {
2915
3154
  const value = (this.value() ?? '').trim();
2916
3155
  if (!value) {
2917
3156
  return 'empty';
2918
3157
  }
2919
- const all = [...this.stageTargets(), ...this.references()];
3158
+ const all = this.references();
2920
3159
  if (all.some((reference) => reference.name === value)) {
2921
3160
  return 'known';
2922
3161
  }
2923
3162
  const root = referenceRoot(value);
2924
- const global = this.dictionaries
2925
- .globalVariables()
2926
- .find((entry) => entry.scope === root || entry.value === root);
2927
- if (global?.isResolvedByHost) {
3163
+ if (this.dictionaries.globalScope(root) && !this.dictionaries.isGlobalScopeVerifiable(root)) {
2928
3164
  return 'host';
2929
3165
  }
2930
3166
  if (all.some((reference) => reference.name === root)) {
2931
3167
  return 'navigated';
2932
3168
  }
2933
3169
  // Se l'elenco non e' arrivato, non si accusa nessuno.
2934
- return this.references().length === 0 && this.isLoading() ? 'empty' : 'unknown';
3170
+ return all.length === 0 && this.isLoading() ? 'empty' : 'unknown';
2935
3171
  }, ...(ngDevMode ? [{ debugName: "valueState" }] : []));
2936
3172
  hint = computed(() => {
2937
3173
  switch (this.valueState()) {
2938
3174
  case 'host':
2939
- return 'Scope risolto dal sistema ospite: il backend non puo’ verificarne il percorso.';
3175
+ return 'Scope risolto dal sistema ospite, che non dichiara i suoi percorsi: il backend non puo’ verificarlo.';
2940
3176
  case 'navigated':
2941
3177
  return 'Percorso su un riferimento noto: la validazione non verifica i campi dopo il punto.';
2942
3178
  case 'unknown':
@@ -2987,12 +3223,12 @@ class ReferencePickerComponent {
2987
3223
  return parts.join(' · ');
2988
3224
  }
2989
3225
  static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.3.27", ngImport: i0, type: ReferencePickerComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
2990
- static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "20.3.27", type: ReferencePickerComponent, isStandalone: true, selector: "fb-reference-picker", inputs: { value: { classPropertyName: "value", publicName: "value", isSignal: true, isRequired: false, transformFunction: null }, label: { classPropertyName: "label", publicName: "label", isSignal: true, isRequired: false, transformFunction: null }, placeholder: { classPropertyName: "placeholder", publicName: "placeholder", isSignal: true, isRequired: false, transformFunction: null }, disabled: { classPropertyName: "disabled", publicName: "disabled", isSignal: true, isRequired: false, transformFunction: null }, dataType: { classPropertyName: "dataType", publicName: "dataType", isSignal: true, isRequired: false, transformFunction: null }, isCollection: { classPropertyName: "isCollection", publicName: "isCollection", isSignal: true, isRequired: false, transformFunction: null }, objectType: { classPropertyName: "objectType", publicName: "objectType", isSignal: true, isRequired: false, transformFunction: null }, writableOnly: { classPropertyName: "writableOnly", publicName: "writableOnly", isSignal: true, isRequired: false, transformFunction: null }, allowStageTargets: { classPropertyName: "allowStageTargets", publicName: "allowStageTargets", isSignal: true, isRequired: false, transformFunction: null }, elementsOnly: { classPropertyName: "elementsOnly", publicName: "elementsOnly", isSignal: true, isRequired: false, transformFunction: null } }, outputs: { valueChange: "valueChange" }, ngImport: i0, template: "<div class=\"fb-ref\" [class.fb-ref--open]=\"isOpen()\">\n <div class=\"fb-ref__control\">\n <input\n class=\"fb-ref__input\"\n type=\"text\"\n [value]=\"value() || ''\"\n [placeholder]=\"placeholder()\"\n [disabled]=\"disabled()\"\n [attr.aria-label]=\"label()\"\n (input)=\"onManualInput($any($event.target).value)\"\n (focus)=\"open()\"\n />\n <button\n type=\"button\"\n class=\"fb-ref__toggle\"\n [disabled]=\"disabled()\"\n [attr.aria-expanded]=\"isOpen()\"\n aria-label=\"Mostra i riferimenti disponibili\"\n (click)=\"toggle()\"\n >\n \u25BE\n </button>\n @if (value()) {\n <button type=\"button\" class=\"fb-ref__clear\" aria-label=\"Svuota\" (click)=\"clear()\">\u00D7</button>\n }\n </div>\n\n @if (hint()) {\n <p class=\"fb-ref__hint\" [class.fb-ref__hint--warn]=\"valueState() === 'unknown'\">{{ hint() }}</p>\n }\n @if (errorMessage()) {\n <p class=\"fb-ref__hint fb-ref__hint--warn\">\n Elenco dei riferimenti non disponibile: puoi scrivere il nome a mano.\n </p>\n }\n\n @if (isOpen()) {\n <div class=\"fb-ref__panel\" role=\"listbox\">\n <input\n class=\"fb-ref__search\"\n type=\"search\"\n placeholder=\"Filtra\"\n aria-label=\"Filtra i riferimenti\"\n (input)=\"onQuery($any($event.target).value)\"\n />\n @if (groups().length === 0) {\n <p class=\"fb-ref__empty\">Nessun riferimento compatibile.</p>\n }\n @for (group of groups(); track group.kind) {\n <div class=\"fb-ref__group\">\n <span class=\"fb-ref__group-label\">{{ group.label }}</span>\n @for (item of group.items; track item.name) {\n <button\n type=\"button\"\n class=\"fb-ref__option\"\n role=\"option\"\n [attr.aria-selected]=\"item.name === value()\"\n (click)=\"choose(item)\"\n >\n <span class=\"fb-ref__name\">{{ item.name }}</span>\n <span class=\"fb-ref__meta\">{{ describe(item) }}</span>\n </button>\n }\n </div>\n }\n <button type=\"button\" class=\"fb-ref__close\" (click)=\"close()\">Chiudi</button>\n </div>\n }\n</div>\n", styles: [":host{display:block}.fb-ref{position:relative}.fb-ref__control{display:flex;align-items:stretch;gap:0;border:1px solid var(--fb-border, #d6dae1);border-radius:6px;background:var(--fb-surface, #fff);overflow:hidden}.fb-ref--open .fb-ref__control{border-color:var(--fb-accent, #2f6feb)}.fb-ref__input{flex:1;min-width:0;padding:5px 7px;border:0;background:transparent;color:var(--fb-text, #1d2939);font:inherit;font-size:12px;font-family:ui-monospace,SFMono-Regular,Menlo,monospace}.fb-ref__input:focus-visible{outline:none}.fb-ref__toggle,.fb-ref__clear{flex:0 0 auto;padding:0 7px;border:0;border-left:1px solid var(--fb-border-subtle, #e6e9ee);background:transparent;color:var(--fb-text-muted, #667085);font:inherit;font-size:12px;cursor:pointer}.fb-ref__toggle:hover,.fb-ref__clear:hover{background:var(--fb-surface-alt, #f8f9fb)}.fb-ref__hint{margin:3px 0 0;font-size:10px;line-height:1.35;color:var(--fb-text-subtle, #98a2b3)}.fb-ref__hint--warn{color:var(--fb-warning, #b7791f)}.fb-ref__panel{position:absolute;z-index:30;top:calc(100% + 3px);left:0;right:0;max-height:260px;overflow-y:auto;padding:6px;border:1px solid var(--fb-border, #d6dae1);border-radius:6px;background:var(--fb-surface, #fff);box-shadow:0 6px 18px #10182829}.fb-ref__search{box-sizing:border-box;width:100%;margin-bottom:6px;padding:4px 6px;border:1px solid var(--fb-border, #d6dae1);border-radius:5px;font:inherit;font-size:12px}.fb-ref__group{margin-bottom:6px}.fb-ref__group-label{display:block;padding:2px 4px;font-size:9px;font-weight:700;letter-spacing:.05em;text-transform:uppercase;color:var(--fb-text-subtle, #98a2b3)}.fb-ref__option{display:flex;flex-direction:column;width:100%;padding:4px 6px;border:0;border-radius:4px;background:transparent;color:var(--fb-text, #1d2939);font:inherit;text-align:left;cursor:pointer}.fb-ref__option:hover,.fb-ref__option[aria-selected=true]{background:var(--fb-surface-alt, #f8f9fb)}.fb-ref__name{font-family:ui-monospace,SFMono-Regular,Menlo,monospace;font-size:12px}.fb-ref__meta{font-size:10px;color:var(--fb-text-muted, #667085)}.fb-ref__empty{margin:4px;font-size:11px;color:var(--fb-text-muted, #667085)}.fb-ref__close{width:100%;margin-top:4px;padding:4px;border:0;border-top:1px solid var(--fb-border-subtle, #e6e9ee);background:transparent;color:var(--fb-text-muted, #667085);font:inherit;font-size:11px;cursor:pointer}\n"], changeDetection: i0.ChangeDetectionStrategy.OnPush });
3226
+ static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "20.3.27", type: ReferencePickerComponent, isStandalone: true, selector: "fb-reference-picker", inputs: { value: { classPropertyName: "value", publicName: "value", isSignal: true, isRequired: false, transformFunction: null }, label: { classPropertyName: "label", publicName: "label", isSignal: true, isRequired: false, transformFunction: null }, placeholder: { classPropertyName: "placeholder", publicName: "placeholder", isSignal: true, isRequired: false, transformFunction: null }, disabled: { classPropertyName: "disabled", publicName: "disabled", isSignal: true, isRequired: false, transformFunction: null }, dataType: { classPropertyName: "dataType", publicName: "dataType", isSignal: true, isRequired: false, transformFunction: null }, isCollection: { classPropertyName: "isCollection", publicName: "isCollection", isSignal: true, isRequired: false, transformFunction: null }, objectType: { classPropertyName: "objectType", publicName: "objectType", isSignal: true, isRequired: false, transformFunction: null }, writableOnly: { classPropertyName: "writableOnly", publicName: "writableOnly", isSignal: true, isRequired: false, transformFunction: null }, elementsOnly: { classPropertyName: "elementsOnly", publicName: "elementsOnly", isSignal: true, isRequired: false, transformFunction: null } }, outputs: { valueChange: "valueChange" }, ngImport: i0, template: "<div class=\"fb-ref\" [class.fb-ref--open]=\"isOpen()\">\r\n <div class=\"fb-ref__control\">\r\n <input\r\n class=\"fb-ref__input\"\r\n type=\"text\"\r\n [value]=\"value() || ''\"\r\n [placeholder]=\"placeholder()\"\r\n [disabled]=\"disabled()\"\r\n [attr.aria-label]=\"label()\"\r\n (input)=\"onManualInput($any($event.target).value)\"\r\n (focus)=\"open()\"\r\n />\r\n <button\r\n type=\"button\"\r\n class=\"fb-ref__toggle\"\r\n [disabled]=\"disabled()\"\r\n [attr.aria-expanded]=\"isOpen()\"\r\n aria-label=\"Mostra i riferimenti disponibili\"\r\n (click)=\"toggle()\"\r\n >\r\n \u25BE\r\n </button>\r\n @if (value()) {\r\n <button type=\"button\" class=\"fb-ref__clear\" aria-label=\"Svuota\" (click)=\"clear()\">\u00D7</button>\r\n }\r\n </div>\r\n\r\n @if (hint()) {\r\n <p class=\"fb-ref__hint\" [class.fb-ref__hint--warn]=\"valueState() === 'unknown'\">{{ hint() }}</p>\r\n }\r\n @if (errorMessage()) {\r\n <p class=\"fb-ref__hint fb-ref__hint--warn\">\r\n Elenco dei riferimenti non disponibile: puoi scrivere il nome a mano.\r\n </p>\r\n }\r\n\r\n @if (isOpen()) {\r\n <div class=\"fb-ref__panel\" role=\"listbox\">\r\n <input\r\n class=\"fb-ref__search\"\r\n type=\"search\"\r\n placeholder=\"Filtra\"\r\n aria-label=\"Filtra i riferimenti\"\r\n (input)=\"onQuery($any($event.target).value)\"\r\n />\r\n @if (groups().length === 0) {\r\n <p class=\"fb-ref__empty\">Nessun riferimento compatibile.</p>\r\n }\r\n @for (group of groups(); track group.kind) {\r\n <div class=\"fb-ref__group\">\r\n <span class=\"fb-ref__group-label\">{{ group.label }}</span>\r\n @for (item of group.items; track item.name) {\r\n <button\r\n type=\"button\"\r\n class=\"fb-ref__option\"\r\n role=\"option\"\r\n [attr.aria-selected]=\"item.name === value()\"\r\n (click)=\"choose(item)\"\r\n >\r\n <span class=\"fb-ref__name\">{{ item.name }}</span>\r\n <span class=\"fb-ref__meta\">{{ describe(item) }}</span>\r\n </button>\r\n }\r\n </div>\r\n }\r\n <button type=\"button\" class=\"fb-ref__close\" (click)=\"close()\">Chiudi</button>\r\n </div>\r\n }\r\n</div>\r\n", styles: [":host{display:block}.fb-ref{position:relative}.fb-ref__control{display:flex;align-items:stretch;gap:0;border:1px solid var(--fb-border, #d6dae1);border-radius:6px;background:var(--fb-surface, #fff);overflow:hidden}.fb-ref--open .fb-ref__control{border-color:var(--fb-accent, #2f6feb)}.fb-ref__input{flex:1;min-width:0;padding:5px 7px;border:0;background:transparent;color:var(--fb-text, #1d2939);font:inherit;font-size:12px;font-family:ui-monospace,SFMono-Regular,Menlo,monospace}.fb-ref__input:focus-visible{outline:none}.fb-ref__toggle,.fb-ref__clear{flex:0 0 auto;padding:0 7px;border:0;border-left:1px solid var(--fb-border-subtle, #e6e9ee);background:transparent;color:var(--fb-text-muted, #667085);font:inherit;font-size:12px;cursor:pointer}.fb-ref__toggle:hover,.fb-ref__clear:hover{background:var(--fb-surface-alt, #f8f9fb)}.fb-ref__hint{margin:3px 0 0;font-size:10px;line-height:1.35;color:var(--fb-text-subtle, #98a2b3)}.fb-ref__hint--warn{color:var(--fb-warning, #b7791f)}.fb-ref__panel{position:absolute;z-index:30;top:calc(100% + 3px);left:0;right:0;max-height:260px;overflow-y:auto;padding:6px;border:1px solid var(--fb-border, #d6dae1);border-radius:6px;background:var(--fb-surface, #fff);box-shadow:0 6px 18px #10182829}.fb-ref__search{box-sizing:border-box;width:100%;margin-bottom:6px;padding:4px 6px;border:1px solid var(--fb-border, #d6dae1);border-radius:5px;font:inherit;font-size:12px}.fb-ref__group{margin-bottom:6px}.fb-ref__group-label{display:block;padding:2px 4px;font-size:9px;font-weight:700;letter-spacing:.05em;text-transform:uppercase;color:var(--fb-text-subtle, #98a2b3)}.fb-ref__option{display:flex;flex-direction:column;width:100%;padding:4px 6px;border:0;border-radius:4px;background:transparent;color:var(--fb-text, #1d2939);font:inherit;text-align:left;cursor:pointer}.fb-ref__option:hover,.fb-ref__option[aria-selected=true]{background:var(--fb-surface-alt, #f8f9fb)}.fb-ref__name{font-family:ui-monospace,SFMono-Regular,Menlo,monospace;font-size:12px}.fb-ref__meta{font-size:10px;color:var(--fb-text-muted, #667085)}.fb-ref__empty{margin:4px;font-size:11px;color:var(--fb-text-muted, #667085)}.fb-ref__close{width:100%;margin-top:4px;padding:4px;border:0;border-top:1px solid var(--fb-border-subtle, #e6e9ee);background:transparent;color:var(--fb-text-muted, #667085);font:inherit;font-size:11px;cursor:pointer}\n"], changeDetection: i0.ChangeDetectionStrategy.OnPush });
2991
3227
  }
2992
3228
  i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.27", ngImport: i0, type: ReferencePickerComponent, decorators: [{
2993
3229
  type: Component,
2994
- args: [{ selector: 'fb-reference-picker', standalone: true, changeDetection: ChangeDetectionStrategy.OnPush, template: "<div class=\"fb-ref\" [class.fb-ref--open]=\"isOpen()\">\n <div class=\"fb-ref__control\">\n <input\n class=\"fb-ref__input\"\n type=\"text\"\n [value]=\"value() || ''\"\n [placeholder]=\"placeholder()\"\n [disabled]=\"disabled()\"\n [attr.aria-label]=\"label()\"\n (input)=\"onManualInput($any($event.target).value)\"\n (focus)=\"open()\"\n />\n <button\n type=\"button\"\n class=\"fb-ref__toggle\"\n [disabled]=\"disabled()\"\n [attr.aria-expanded]=\"isOpen()\"\n aria-label=\"Mostra i riferimenti disponibili\"\n (click)=\"toggle()\"\n >\n \u25BE\n </button>\n @if (value()) {\n <button type=\"button\" class=\"fb-ref__clear\" aria-label=\"Svuota\" (click)=\"clear()\">\u00D7</button>\n }\n </div>\n\n @if (hint()) {\n <p class=\"fb-ref__hint\" [class.fb-ref__hint--warn]=\"valueState() === 'unknown'\">{{ hint() }}</p>\n }\n @if (errorMessage()) {\n <p class=\"fb-ref__hint fb-ref__hint--warn\">\n Elenco dei riferimenti non disponibile: puoi scrivere il nome a mano.\n </p>\n }\n\n @if (isOpen()) {\n <div class=\"fb-ref__panel\" role=\"listbox\">\n <input\n class=\"fb-ref__search\"\n type=\"search\"\n placeholder=\"Filtra\"\n aria-label=\"Filtra i riferimenti\"\n (input)=\"onQuery($any($event.target).value)\"\n />\n @if (groups().length === 0) {\n <p class=\"fb-ref__empty\">Nessun riferimento compatibile.</p>\n }\n @for (group of groups(); track group.kind) {\n <div class=\"fb-ref__group\">\n <span class=\"fb-ref__group-label\">{{ group.label }}</span>\n @for (item of group.items; track item.name) {\n <button\n type=\"button\"\n class=\"fb-ref__option\"\n role=\"option\"\n [attr.aria-selected]=\"item.name === value()\"\n (click)=\"choose(item)\"\n >\n <span class=\"fb-ref__name\">{{ item.name }}</span>\n <span class=\"fb-ref__meta\">{{ describe(item) }}</span>\n </button>\n }\n </div>\n }\n <button type=\"button\" class=\"fb-ref__close\" (click)=\"close()\">Chiudi</button>\n </div>\n }\n</div>\n", styles: [":host{display:block}.fb-ref{position:relative}.fb-ref__control{display:flex;align-items:stretch;gap:0;border:1px solid var(--fb-border, #d6dae1);border-radius:6px;background:var(--fb-surface, #fff);overflow:hidden}.fb-ref--open .fb-ref__control{border-color:var(--fb-accent, #2f6feb)}.fb-ref__input{flex:1;min-width:0;padding:5px 7px;border:0;background:transparent;color:var(--fb-text, #1d2939);font:inherit;font-size:12px;font-family:ui-monospace,SFMono-Regular,Menlo,monospace}.fb-ref__input:focus-visible{outline:none}.fb-ref__toggle,.fb-ref__clear{flex:0 0 auto;padding:0 7px;border:0;border-left:1px solid var(--fb-border-subtle, #e6e9ee);background:transparent;color:var(--fb-text-muted, #667085);font:inherit;font-size:12px;cursor:pointer}.fb-ref__toggle:hover,.fb-ref__clear:hover{background:var(--fb-surface-alt, #f8f9fb)}.fb-ref__hint{margin:3px 0 0;font-size:10px;line-height:1.35;color:var(--fb-text-subtle, #98a2b3)}.fb-ref__hint--warn{color:var(--fb-warning, #b7791f)}.fb-ref__panel{position:absolute;z-index:30;top:calc(100% + 3px);left:0;right:0;max-height:260px;overflow-y:auto;padding:6px;border:1px solid var(--fb-border, #d6dae1);border-radius:6px;background:var(--fb-surface, #fff);box-shadow:0 6px 18px #10182829}.fb-ref__search{box-sizing:border-box;width:100%;margin-bottom:6px;padding:4px 6px;border:1px solid var(--fb-border, #d6dae1);border-radius:5px;font:inherit;font-size:12px}.fb-ref__group{margin-bottom:6px}.fb-ref__group-label{display:block;padding:2px 4px;font-size:9px;font-weight:700;letter-spacing:.05em;text-transform:uppercase;color:var(--fb-text-subtle, #98a2b3)}.fb-ref__option{display:flex;flex-direction:column;width:100%;padding:4px 6px;border:0;border-radius:4px;background:transparent;color:var(--fb-text, #1d2939);font:inherit;text-align:left;cursor:pointer}.fb-ref__option:hover,.fb-ref__option[aria-selected=true]{background:var(--fb-surface-alt, #f8f9fb)}.fb-ref__name{font-family:ui-monospace,SFMono-Regular,Menlo,monospace;font-size:12px}.fb-ref__meta{font-size:10px;color:var(--fb-text-muted, #667085)}.fb-ref__empty{margin:4px;font-size:11px;color:var(--fb-text-muted, #667085)}.fb-ref__close{width:100%;margin-top:4px;padding:4px;border:0;border-top:1px solid var(--fb-border-subtle, #e6e9ee);background:transparent;color:var(--fb-text-muted, #667085);font:inherit;font-size:11px;cursor:pointer}\n"] }]
2995
- }], ctorParameters: () => [], propDecorators: { value: [{ type: i0.Input, args: [{ isSignal: true, alias: "value", required: false }] }], label: [{ type: i0.Input, args: [{ isSignal: true, alias: "label", required: false }] }], placeholder: [{ type: i0.Input, args: [{ isSignal: true, alias: "placeholder", required: false }] }], disabled: [{ type: i0.Input, args: [{ isSignal: true, alias: "disabled", required: false }] }], dataType: [{ type: i0.Input, args: [{ isSignal: true, alias: "dataType", required: false }] }], isCollection: [{ type: i0.Input, args: [{ isSignal: true, alias: "isCollection", required: false }] }], objectType: [{ type: i0.Input, args: [{ isSignal: true, alias: "objectType", required: false }] }], writableOnly: [{ type: i0.Input, args: [{ isSignal: true, alias: "writableOnly", required: false }] }], allowStageTargets: [{ type: i0.Input, args: [{ isSignal: true, alias: "allowStageTargets", required: false }] }], elementsOnly: [{ type: i0.Input, args: [{ isSignal: true, alias: "elementsOnly", required: false }] }], valueChange: [{ type: i0.Output, args: ["valueChange"] }] } });
3230
+ args: [{ selector: 'fb-reference-picker', standalone: true, changeDetection: ChangeDetectionStrategy.OnPush, template: "<div class=\"fb-ref\" [class.fb-ref--open]=\"isOpen()\">\r\n <div class=\"fb-ref__control\">\r\n <input\r\n class=\"fb-ref__input\"\r\n type=\"text\"\r\n [value]=\"value() || ''\"\r\n [placeholder]=\"placeholder()\"\r\n [disabled]=\"disabled()\"\r\n [attr.aria-label]=\"label()\"\r\n (input)=\"onManualInput($any($event.target).value)\"\r\n (focus)=\"open()\"\r\n />\r\n <button\r\n type=\"button\"\r\n class=\"fb-ref__toggle\"\r\n [disabled]=\"disabled()\"\r\n [attr.aria-expanded]=\"isOpen()\"\r\n aria-label=\"Mostra i riferimenti disponibili\"\r\n (click)=\"toggle()\"\r\n >\r\n \u25BE\r\n </button>\r\n @if (value()) {\r\n <button type=\"button\" class=\"fb-ref__clear\" aria-label=\"Svuota\" (click)=\"clear()\">\u00D7</button>\r\n }\r\n </div>\r\n\r\n @if (hint()) {\r\n <p class=\"fb-ref__hint\" [class.fb-ref__hint--warn]=\"valueState() === 'unknown'\">{{ hint() }}</p>\r\n }\r\n @if (errorMessage()) {\r\n <p class=\"fb-ref__hint fb-ref__hint--warn\">\r\n Elenco dei riferimenti non disponibile: puoi scrivere il nome a mano.\r\n </p>\r\n }\r\n\r\n @if (isOpen()) {\r\n <div class=\"fb-ref__panel\" role=\"listbox\">\r\n <input\r\n class=\"fb-ref__search\"\r\n type=\"search\"\r\n placeholder=\"Filtra\"\r\n aria-label=\"Filtra i riferimenti\"\r\n (input)=\"onQuery($any($event.target).value)\"\r\n />\r\n @if (groups().length === 0) {\r\n <p class=\"fb-ref__empty\">Nessun riferimento compatibile.</p>\r\n }\r\n @for (group of groups(); track group.kind) {\r\n <div class=\"fb-ref__group\">\r\n <span class=\"fb-ref__group-label\">{{ group.label }}</span>\r\n @for (item of group.items; track item.name) {\r\n <button\r\n type=\"button\"\r\n class=\"fb-ref__option\"\r\n role=\"option\"\r\n [attr.aria-selected]=\"item.name === value()\"\r\n (click)=\"choose(item)\"\r\n >\r\n <span class=\"fb-ref__name\">{{ item.name }}</span>\r\n <span class=\"fb-ref__meta\">{{ describe(item) }}</span>\r\n </button>\r\n }\r\n </div>\r\n }\r\n <button type=\"button\" class=\"fb-ref__close\" (click)=\"close()\">Chiudi</button>\r\n </div>\r\n }\r\n</div>\r\n", styles: [":host{display:block}.fb-ref{position:relative}.fb-ref__control{display:flex;align-items:stretch;gap:0;border:1px solid var(--fb-border, #d6dae1);border-radius:6px;background:var(--fb-surface, #fff);overflow:hidden}.fb-ref--open .fb-ref__control{border-color:var(--fb-accent, #2f6feb)}.fb-ref__input{flex:1;min-width:0;padding:5px 7px;border:0;background:transparent;color:var(--fb-text, #1d2939);font:inherit;font-size:12px;font-family:ui-monospace,SFMono-Regular,Menlo,monospace}.fb-ref__input:focus-visible{outline:none}.fb-ref__toggle,.fb-ref__clear{flex:0 0 auto;padding:0 7px;border:0;border-left:1px solid var(--fb-border-subtle, #e6e9ee);background:transparent;color:var(--fb-text-muted, #667085);font:inherit;font-size:12px;cursor:pointer}.fb-ref__toggle:hover,.fb-ref__clear:hover{background:var(--fb-surface-alt, #f8f9fb)}.fb-ref__hint{margin:3px 0 0;font-size:10px;line-height:1.35;color:var(--fb-text-subtle, #98a2b3)}.fb-ref__hint--warn{color:var(--fb-warning, #b7791f)}.fb-ref__panel{position:absolute;z-index:30;top:calc(100% + 3px);left:0;right:0;max-height:260px;overflow-y:auto;padding:6px;border:1px solid var(--fb-border, #d6dae1);border-radius:6px;background:var(--fb-surface, #fff);box-shadow:0 6px 18px #10182829}.fb-ref__search{box-sizing:border-box;width:100%;margin-bottom:6px;padding:4px 6px;border:1px solid var(--fb-border, #d6dae1);border-radius:5px;font:inherit;font-size:12px}.fb-ref__group{margin-bottom:6px}.fb-ref__group-label{display:block;padding:2px 4px;font-size:9px;font-weight:700;letter-spacing:.05em;text-transform:uppercase;color:var(--fb-text-subtle, #98a2b3)}.fb-ref__option{display:flex;flex-direction:column;width:100%;padding:4px 6px;border:0;border-radius:4px;background:transparent;color:var(--fb-text, #1d2939);font:inherit;text-align:left;cursor:pointer}.fb-ref__option:hover,.fb-ref__option[aria-selected=true]{background:var(--fb-surface-alt, #f8f9fb)}.fb-ref__name{font-family:ui-monospace,SFMono-Regular,Menlo,monospace;font-size:12px}.fb-ref__meta{font-size:10px;color:var(--fb-text-muted, #667085)}.fb-ref__empty{margin:4px;font-size:11px;color:var(--fb-text-muted, #667085)}.fb-ref__close{width:100%;margin-top:4px;padding:4px;border:0;border-top:1px solid var(--fb-border-subtle, #e6e9ee);background:transparent;color:var(--fb-text-muted, #667085);font:inherit;font-size:11px;cursor:pointer}\n"] }]
3231
+ }], ctorParameters: () => [], propDecorators: { value: [{ type: i0.Input, args: [{ isSignal: true, alias: "value", required: false }] }], label: [{ type: i0.Input, args: [{ isSignal: true, alias: "label", required: false }] }], placeholder: [{ type: i0.Input, args: [{ isSignal: true, alias: "placeholder", required: false }] }], disabled: [{ type: i0.Input, args: [{ isSignal: true, alias: "disabled", required: false }] }], dataType: [{ type: i0.Input, args: [{ isSignal: true, alias: "dataType", required: false }] }], isCollection: [{ type: i0.Input, args: [{ isSignal: true, alias: "isCollection", required: false }] }], objectType: [{ type: i0.Input, args: [{ isSignal: true, alias: "objectType", required: false }] }], writableOnly: [{ type: i0.Input, args: [{ isSignal: true, alias: "writableOnly", required: false }] }], elementsOnly: [{ type: i0.Input, args: [{ isSignal: true, alias: "elementsOnly", required: false }] }], valueChange: [{ type: i0.Output, args: ["valueChange"] }] } });
2996
3232
 
2997
3233
  /**
2998
3234
  * `[fbValue]` su un `<select>`.
@@ -3042,6 +3278,14 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.27", ngImpo
3042
3278
  *
3043
3279
  * Il tipo del letterale deve corrispondere al tipo della destinazione: quando il tipo e'
3044
3280
  * noto, l'editor mostra il controllo giusto e propone quella modalita' per default.
3281
+ *
3282
+ * Due dettagli di formato che il contratto tratta come sostanza, non come presentazione:
3283
+ *
3284
+ * - i numeri viaggiano in **cultura invariante** (§4.3): il separatore decimale e' il punto, e
3285
+ * `1234,50` viene tradotto in `1234.50` invece di essere troncato in silenzio;
3286
+ * - un valore data **senza fuso** significa "ora locale dell'applicazione", non "UTC"
3287
+ * (§4.2, §13.15): sono due istanti diversi, quindi il fuso e' una scelta esplicita
3288
+ * dell'utente e non un effetto collaterale del datepicker.
3045
3289
  */
3046
3290
  class ValueEditorComponent {
3047
3291
  dictionaries = inject(FlowDictionaryStore);
@@ -3147,6 +3391,12 @@ class ValueEditorComponent {
3147
3391
  return '';
3148
3392
  }
3149
3393
  const field = this.literalField();
3394
+ if (field === 'dateValue' && value.dateValue !== undefined) {
3395
+ // `datetime-local` rifiuta un valore col fuso e resterebbe **vuoto** pur avendo un
3396
+ // valore: il suffisso si toglie qui e si riscrive in uscita. Se il documento porta un
3397
+ // campo diverso da quello atteso si passa al fallback qui sotto, che lo mostra comunque.
3398
+ return ValueEditorComponent.stripZone(value.dateValue);
3399
+ }
3150
3400
  const raw = value[field];
3151
3401
  if (raw === undefined || raw === null) {
3152
3402
  // Se il documento porta un tipo diverso da quello atteso, mostralo comunque:
@@ -3161,6 +3411,15 @@ class ValueEditorComponent {
3161
3411
  return String(raw);
3162
3412
  }, ...(ngDevMode ? [{ debugName: "literalText" }] : []));
3163
3413
  booleanValue = computed(() => this.value()?.booleanValue === true, ...(ngDevMode ? [{ debugName: "booleanValue" }] : []));
3414
+ /** Il fuso dichiarato nel valore data: `Z`, un offset, oppure niente = ora locale (§4.2). */
3415
+ dateIsUtc = computed(() => ValueEditorComponent.hasZone(this.value()?.dateValue), ...(ngDevMode ? [{ debugName: "dateIsUtc" }] : []));
3416
+ static ZONE_PATTERN = /(Z|[+-]\d{2}:?\d{2})$/;
3417
+ static hasZone(value) {
3418
+ return !!value && ValueEditorComponent.ZONE_PATTERN.test(value);
3419
+ }
3420
+ static stripZone(value) {
3421
+ return (value ?? '').replace(ValueEditorComponent.ZONE_PATTERN, '');
3422
+ }
3164
3423
  globalConstants = computed(() => {
3165
3424
  const entry = this.dictionaries
3166
3425
  .globalVariables()
@@ -3169,7 +3428,17 @@ class ValueEditorComponent {
3169
3428
  // fanno digitare (§4.1).
3170
3429
  return (entry?.paths ?? []).map((path) => `$GlobalConstant.${path}`);
3171
3430
  }, ...(ngDevMode ? [{ debugName: "globalConstants" }] : []));
3172
- numericScaleHint = computed(() => this.dataType() === 'Number' ? 'Usa Number per importi e quantita’: Integer troncherebbe i decimali.' : null, ...(ngDevMode ? [{ debugName: "numericScaleHint" }] : []));
3431
+ numericHint = computed(() => {
3432
+ switch (this.dataType()) {
3433
+ case 'Number':
3434
+ // Il punto decimale non e' una preferenza di formato: la virgola non e' accettata (§4.3).
3435
+ return 'Separatore decimale: il punto. Usa Number per importi e quantita’: Integer troncherebbe i decimali.';
3436
+ case 'Integer':
3437
+ return 'Numero intero, senza separatore delle migliaia.';
3438
+ default:
3439
+ return null;
3440
+ }
3441
+ }, ...(ngDevMode ? [{ debugName: "numericHint" }] : []));
3173
3442
  setMode(mode) {
3174
3443
  switch (mode) {
3175
3444
  case 'reference':
@@ -3201,19 +3470,21 @@ class ValueEditorComponent {
3201
3470
  }
3202
3471
  switch (field) {
3203
3472
  case 'integerValue': {
3204
- const parsed = Number.parseInt(raw, 10);
3205
- this.emit(Number.isNaN(parsed) ? undefined : { integerValue: parsed });
3473
+ // Cultura invariante anche sugli interi: `1.234` non e' milleduecentotrentaquattro.
3474
+ const parsed = parseInvariantNumber(raw);
3475
+ this.emit(parsed === undefined ? undefined : { integerValue: Math.trunc(parsed) });
3206
3476
  return;
3207
3477
  }
3208
3478
  case 'numberValue': {
3209
- const parsed = Number.parseFloat(raw);
3210
- this.emit(Number.isNaN(parsed) ? undefined : { numberValue: parsed });
3479
+ const parsed = parseInvariantNumber(raw);
3480
+ this.emit(parsed === undefined ? undefined : { numberValue: parsed });
3211
3481
  return;
3212
3482
  }
3213
3483
  case 'dateValue':
3214
- // Formato roundtrip senza fuso, come chiede il contratto (§2): l'input
3215
- // `datetime-local` produce già `2026-08-15T00:00`, si completano i secondi.
3216
- this.emit({ dateValue: raw.length === 16 ? `${raw}:00` : raw });
3484
+ // L'input `datetime-local` produce `2026-08-15T00:00` senza fuso: si completano i
3485
+ // secondi e si riscrive il fuso scelto, perche' senza `Z` il valore significa
3486
+ // "ora locale" e il motore lo converte prima di confrontarlo (§4.2).
3487
+ this.emit({ dateValue: this.withDateZone(raw.length === 16 ? `${raw}:00` : raw) });
3217
3488
  return;
3218
3489
  case 'enumValue':
3219
3490
  this.emit({ enumValue: raw });
@@ -3225,6 +3496,21 @@ class ValueEditorComponent {
3225
3496
  onBooleanChange(checked) {
3226
3497
  this.emit({ booleanValue: checked });
3227
3498
  }
3499
+ withDateZone(local, utc = this.dateIsUtc()) {
3500
+ return utc ? `${ValueEditorComponent.stripZone(local)}Z` : ValueEditorComponent.stripZone(local);
3501
+ }
3502
+ /**
3503
+ * Cambia il fuso **dichiarato**, non l'istante: `09:00Z` e `09:00` sono le nove in due fusi
3504
+ * diversi, e l'editor non converte al posto dell'utente. Riscrivere lo stesso orario con
3505
+ * l'altro fuso e' esattamente cio' che serve quando il valore e' stato inserito sbagliato.
3506
+ */
3507
+ setDateZone(utc) {
3508
+ const current = this.value()?.dateValue;
3509
+ if (!current) {
3510
+ return;
3511
+ }
3512
+ this.emit({ dateValue: this.withDateZone(current, utc) });
3513
+ }
3228
3514
  onFormulaChange(expression) {
3229
3515
  this.emit({
3230
3516
  formulaExpression: expression,
@@ -3268,17 +3554,17 @@ class ValueEditorComponent {
3268
3554
  }
3269
3555
  dataTypeOptions = computed(() => this.dictionaries.dataTypes(), ...(ngDevMode ? [{ debugName: "dataTypeOptions" }] : []));
3270
3556
  static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.3.27", ngImport: i0, type: ValueEditorComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
3271
- static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "20.3.27", type: ValueEditorComponent, isStandalone: true, selector: "fb-value-editor", inputs: { value: { classPropertyName: "value", publicName: "value", isSignal: true, isRequired: false, transformFunction: null }, label: { classPropertyName: "label", publicName: "label", isSignal: true, isRequired: false, transformFunction: null }, dataType: { classPropertyName: "dataType", publicName: "dataType", isSignal: true, isRequired: false, transformFunction: null }, objectType: { classPropertyName: "objectType", publicName: "objectType", isSignal: true, isRequired: false, transformFunction: null }, isCollection: { classPropertyName: "isCollection", publicName: "isCollection", isSignal: true, isRequired: false, transformFunction: null }, disabled: { classPropertyName: "disabled", publicName: "disabled", isSignal: true, isRequired: false, transformFunction: null }, allowFormula: { classPropertyName: "allowFormula", publicName: "allowFormula", isSignal: true, isRequired: false, transformFunction: null } }, outputs: { valueChange: "valueChange" }, ngImport: i0, template: "<div class=\"fb-value\">\r\n <div class=\"fb-value__modes\" role=\"group\" [attr.aria-label]=\"label() + ': modalita\u2019'\">\r\n <button\r\n type=\"button\"\r\n class=\"fb-value__mode\"\r\n [class.fb-value__mode--active]=\"mode() === 'reference'\"\r\n [disabled]=\"disabled()\"\r\n title=\"Riferimento a una risorsa o all\u2019output di un elemento\"\r\n (click)=\"setMode('reference')\"\r\n >\r\n Riferimento\r\n </button>\r\n @if (literalAllowed()) {\r\n <button\r\n type=\"button\"\r\n class=\"fb-value__mode\"\r\n [class.fb-value__mode--active]=\"mode() === 'literal'\"\r\n [disabled]=\"disabled()\"\r\n title=\"Valore letterale del tipo della destinazione\"\r\n (click)=\"setMode('literal')\"\r\n >\r\n Valore\r\n </button>\r\n }\r\n @if (allowFormula()) {\r\n <button\r\n type=\"button\"\r\n class=\"fb-value__mode\"\r\n [class.fb-value__mode--active]=\"mode() === 'formula'\"\r\n [disabled]=\"disabled()\"\r\n title=\"Espressione calcolata dal motore di regole\"\r\n (click)=\"setMode('formula')\"\r\n >\r\n Formula\r\n </button>\r\n }\r\n @if (globalConstants().length) {\r\n <button\r\n type=\"button\"\r\n class=\"fb-value__mode\"\r\n [class.fb-value__mode--active]=\"mode() === 'globalConstant'\"\r\n [disabled]=\"disabled()\"\r\n title=\"Costante globale\"\r\n (click)=\"setMode('globalConstant')\"\r\n >\r\n Costante globale\r\n </button>\r\n }\r\n @if (mode() !== 'empty') {\r\n <button\r\n type=\"button\"\r\n class=\"fb-value__mode fb-value__mode--clear\"\r\n [disabled]=\"disabled()\"\r\n title=\"Nessun valore\"\r\n (click)=\"setMode('empty')\"\r\n >\r\n \u00D7\r\n </button>\r\n }\r\n </div>\r\n\r\n @if (isAmbiguous()) {\r\n <!-- Piu' campi di valore insieme: il comportamento a runtime dipende dall'ordine di lettura. -->\r\n <p class=\"fb-callout fb-callout--warn\">\r\n Questo valore ha {{ filledFieldCount() }} campi valorizzati insieme: a runtime conta l\u2019ordine di lettura.\r\n <button type=\"button\" class=\"fb-btn fb-btn--ghost fb-btn--icon\" (click)=\"normalize()\">\r\n Tieni solo \u00AB{{ mode() === 'formula' ? 'formula' : mode() === 'literal' ? 'valore' : 'riferimento' }}\u00BB\r\n </button>\r\n </p>\r\n }\r\n\r\n @switch (mode()) {\r\n @case ('reference') {\r\n <fb-reference-picker\r\n [value]=\"value()?.elementReference\"\r\n [label]=\"label()\"\r\n [dataType]=\"dataType()\"\r\n [isCollection]=\"isCollection()\"\r\n [objectType]=\"objectType()\"\r\n [disabled]=\"disabled()\"\r\n (valueChange)=\"onReferenceChange($event)\"\r\n />\r\n }\r\n\r\n @case ('globalConstant') {\r\n <select\r\n class=\"fb-select\"\r\n [disabled]=\"disabled()\"\r\n [fbValue]=\"value()?.elementReference || ''\"\r\n (change)=\"onGlobalConstantChange($any($event.target).value)\"\r\n >\r\n @for (constant of globalConstants(); track constant) {\r\n <option [value]=\"constant\">{{ constant }}</option>\r\n }\r\n </select>\r\n }\r\n\r\n @case ('literal') {\r\n @if (dataType() === 'Boolean') {\r\n <label class=\"fb-check\">\r\n <input\r\n type=\"checkbox\"\r\n [checked]=\"booleanValue()\"\r\n [disabled]=\"disabled()\"\r\n (change)=\"onBooleanChange($any($event.target).checked)\"\r\n />\r\n {{ booleanValue() ? 'vero' : 'falso' }}\r\n </label>\r\n } @else {\r\n <input\r\n class=\"fb-input\"\r\n [type]=\"literalInputType()\"\r\n [value]=\"literalText()\"\r\n [disabled]=\"disabled()\"\r\n [attr.aria-label]=\"label()\"\r\n [placeholder]=\"dataType() === 'Enum' ? 'Nome del valore di enum' : ''\"\r\n (input)=\"onLiteralChange($any($event.target).value)\"\r\n />\r\n @if (numericScaleHint()) {\r\n <p class=\"fb-field__hint\">{{ numericScaleHint() }}</p>\r\n }\r\n @if (dataType() === 'Date') {\r\n <p class=\"fb-field__hint\">\r\n Data e ora insieme. Il backend non converte i fusi: scrivi il valore che il motore deve vedere.\r\n </p>\r\n }\r\n }\r\n }\r\n\r\n @case ('formula') {\r\n <textarea\r\n class=\"fb-textarea fb-input--mono\"\r\n [value]=\"value()?.formulaExpression || ''\"\r\n [disabled]=\"disabled()\"\r\n placeholder=\"Importo * 1.22\"\r\n [attr.aria-label]=\"label() + ': espressione'\"\r\n (input)=\"onFormulaChange($any($event.target).value)\"\r\n ></textarea>\r\n <div class=\"fb-field__row\">\r\n <label class=\"fb-field__hint\">Tipo del risultato</label>\r\n <select\r\n class=\"fb-select\"\r\n [fbValue]=\"value()?.formulaDataType || ''\"\r\n [disabled]=\"disabled()\"\r\n (change)=\"onFormulaTypeChange($any($event.target).value)\"\r\n >\r\n @for (type of dataTypeOptions(); track type.value) {\r\n <option [value]=\"type.value\">{{ type.label }}</option>\r\n }\r\n </select>\r\n </div>\r\n <p class=\"fb-field__hint\">\r\n L\u2019espressione va al motore di regole: il backend non ne verifica la sintassi.\r\n </p>\r\n }\r\n\r\n @case ('empty') {\r\n <p class=\"fb-field__hint\">Nessun valore.</p>\r\n }\r\n }\r\n</div>\r\n", styles: [":host{display:block}.fb-value{display:flex;flex-direction:column;gap:4px}.fb-value__modes{display:flex;flex-wrap:wrap;gap:2px}.fb-value__mode{padding:2px 7px;border:1px solid var(--fb-border-subtle, #e6e9ee);border-radius:10px;background:transparent;color:var(--fb-text-muted, #667085);font:inherit;font-size:10px;cursor:pointer}.fb-value__mode:hover:not(:disabled){background:var(--fb-surface-alt, #f8f9fb)}.fb-value__mode--active{border-color:var(--fb-accent, #2f6feb);background:color-mix(in srgb,var(--fb-accent, #2f6feb) 10%,transparent);color:var(--fb-accent, #2f6feb);font-weight:600}.fb-value__mode--clear{margin-left:auto}.fb-value__mode:disabled{opacity:.5;cursor:not-allowed}\n"], dependencies: [{ kind: "component", type: ReferencePickerComponent, selector: "fb-reference-picker", inputs: ["value", "label", "placeholder", "disabled", "dataType", "isCollection", "objectType", "writableOnly", "allowStageTargets", "elementsOnly"], outputs: ["valueChange"] }, { kind: "directive", type: SelectValueDirective, selector: "select[fbValue]", inputs: ["fbValue"] }], changeDetection: i0.ChangeDetectionStrategy.OnPush });
3557
+ static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "20.3.27", type: ValueEditorComponent, isStandalone: true, selector: "fb-value-editor", inputs: { value: { classPropertyName: "value", publicName: "value", isSignal: true, isRequired: false, transformFunction: null }, label: { classPropertyName: "label", publicName: "label", isSignal: true, isRequired: false, transformFunction: null }, dataType: { classPropertyName: "dataType", publicName: "dataType", isSignal: true, isRequired: false, transformFunction: null }, objectType: { classPropertyName: "objectType", publicName: "objectType", isSignal: true, isRequired: false, transformFunction: null }, isCollection: { classPropertyName: "isCollection", publicName: "isCollection", isSignal: true, isRequired: false, transformFunction: null }, disabled: { classPropertyName: "disabled", publicName: "disabled", isSignal: true, isRequired: false, transformFunction: null }, allowFormula: { classPropertyName: "allowFormula", publicName: "allowFormula", isSignal: true, isRequired: false, transformFunction: null } }, outputs: { valueChange: "valueChange" }, ngImport: i0, template: "<div class=\"fb-value\">\r\n <div class=\"fb-value__modes\" role=\"group\" [attr.aria-label]=\"label() + ': modalita\u2019'\">\r\n <button\r\n type=\"button\"\r\n class=\"fb-value__mode\"\r\n [class.fb-value__mode--active]=\"mode() === 'reference'\"\r\n [disabled]=\"disabled()\"\r\n title=\"Riferimento a una risorsa o all\u2019output di un elemento\"\r\n (click)=\"setMode('reference')\"\r\n >\r\n Riferimento\r\n </button>\r\n @if (literalAllowed()) {\r\n <button\r\n type=\"button\"\r\n class=\"fb-value__mode\"\r\n [class.fb-value__mode--active]=\"mode() === 'literal'\"\r\n [disabled]=\"disabled()\"\r\n title=\"Valore letterale del tipo della destinazione\"\r\n (click)=\"setMode('literal')\"\r\n >\r\n Valore\r\n </button>\r\n }\r\n @if (allowFormula()) {\r\n <button\r\n type=\"button\"\r\n class=\"fb-value__mode\"\r\n [class.fb-value__mode--active]=\"mode() === 'formula'\"\r\n [disabled]=\"disabled()\"\r\n title=\"Espressione calcolata dal motore di regole\"\r\n (click)=\"setMode('formula')\"\r\n >\r\n Formula\r\n </button>\r\n }\r\n @if (globalConstants().length) {\r\n <button\r\n type=\"button\"\r\n class=\"fb-value__mode\"\r\n [class.fb-value__mode--active]=\"mode() === 'globalConstant'\"\r\n [disabled]=\"disabled()\"\r\n title=\"Costante globale\"\r\n (click)=\"setMode('globalConstant')\"\r\n >\r\n Costante globale\r\n </button>\r\n }\r\n @if (mode() !== 'empty') {\r\n <button\r\n type=\"button\"\r\n class=\"fb-value__mode fb-value__mode--clear\"\r\n [disabled]=\"disabled()\"\r\n title=\"Nessun valore\"\r\n (click)=\"setMode('empty')\"\r\n >\r\n \u00D7\r\n </button>\r\n }\r\n </div>\r\n\r\n @if (isAmbiguous()) {\r\n <!-- Piu' campi di valore insieme: il comportamento a runtime dipende dall'ordine di lettura. -->\r\n <p class=\"fb-callout fb-callout--warn\">\r\n Questo valore ha {{ filledFieldCount() }} campi valorizzati insieme: a runtime conta l\u2019ordine di lettura.\r\n <button type=\"button\" class=\"fb-btn fb-btn--ghost fb-btn--icon\" (click)=\"normalize()\">\r\n Tieni solo \u00AB{{ mode() === 'formula' ? 'formula' : mode() === 'literal' ? 'valore' : 'riferimento' }}\u00BB\r\n </button>\r\n </p>\r\n }\r\n\r\n @switch (mode()) {\r\n @case ('reference') {\r\n <fb-reference-picker\r\n [value]=\"value()?.elementReference\"\r\n [label]=\"label()\"\r\n [dataType]=\"dataType()\"\r\n [isCollection]=\"isCollection()\"\r\n [objectType]=\"objectType()\"\r\n [disabled]=\"disabled()\"\r\n (valueChange)=\"onReferenceChange($event)\"\r\n />\r\n }\r\n\r\n @case ('globalConstant') {\r\n <select\r\n class=\"fb-select\"\r\n [disabled]=\"disabled()\"\r\n [fbValue]=\"value()?.elementReference || ''\"\r\n (change)=\"onGlobalConstantChange($any($event.target).value)\"\r\n >\r\n @for (constant of globalConstants(); track constant) {\r\n <option [value]=\"constant\">{{ constant }}</option>\r\n }\r\n </select>\r\n }\r\n\r\n @case ('literal') {\r\n @if (dataType() === 'Boolean') {\r\n <label class=\"fb-check\">\r\n <input\r\n type=\"checkbox\"\r\n [checked]=\"booleanValue()\"\r\n [disabled]=\"disabled()\"\r\n (change)=\"onBooleanChange($any($event.target).checked)\"\r\n />\r\n {{ booleanValue() ? 'vero' : 'falso' }}\r\n </label>\r\n } @else {\r\n <input\r\n class=\"fb-input\"\r\n [type]=\"literalInputType()\"\r\n [value]=\"literalText()\"\r\n [disabled]=\"disabled()\"\r\n [attr.aria-label]=\"label()\"\r\n [placeholder]=\"dataType() === 'Enum' ? 'Nome del valore di enum' : ''\"\r\n (input)=\"onLiteralChange($any($event.target).value)\"\r\n />\r\n @if (numericHint()) {\r\n <p class=\"fb-field__hint\">{{ numericHint() }}</p>\r\n }\r\n @if (dataType() === 'Date') {\r\n <!--\r\n Il fuso e' una scelta, non un dettaglio: `09:00Z` e `09:00` sono due istanti\r\n diversi, e il motore converte in UTC prima di ogni confronto (\u00A74.2).\r\n -->\r\n <div class=\"fb-value__modes\" role=\"group\" aria-label=\"Fuso del valore data\">\r\n <button\r\n type=\"button\"\r\n class=\"fb-value__mode\"\r\n [class.fb-value__mode--active]=\"!dateIsUtc()\"\r\n [disabled]=\"disabled()\"\r\n title=\"Interpretato nel fuso dell\u2019applicazione\"\r\n (click)=\"setDateZone(false)\"\r\n >\r\n Ora locale\r\n </button>\r\n <button\r\n type=\"button\"\r\n class=\"fb-value__mode\"\r\n [class.fb-value__mode--active]=\"dateIsUtc()\"\r\n [disabled]=\"disabled()\"\r\n title=\"Scrive il suffisso Z: l\u2019orario e\u2019 in UTC\"\r\n (click)=\"setDateZone(true)\"\r\n >\r\n UTC (Z)\r\n </button>\r\n </div>\r\n <p class=\"fb-field__hint\">\r\n Data e ora insieme.\r\n {{\r\n dateIsUtc()\r\n ? 'Con \u00ABUTC\u00BB l\u2019orario e\u2019 assoluto.'\r\n : 'Senza fuso l\u2019orario e\u2019 interpretato nel fuso dell\u2019applicazione, non in UTC.'\r\n }}\r\n Cambiare fuso riscrive l\u2019orario, non lo converte.\r\n </p>\r\n }\r\n }\r\n }\r\n\r\n @case ('formula') {\r\n <textarea\r\n class=\"fb-textarea fb-input--mono\"\r\n [value]=\"value()?.formulaExpression || ''\"\r\n [disabled]=\"disabled()\"\r\n placeholder=\"Importo * 1.22\"\r\n [attr.aria-label]=\"label() + ': espressione'\"\r\n (input)=\"onFormulaChange($any($event.target).value)\"\r\n ></textarea>\r\n <div class=\"fb-field__row\">\r\n <label class=\"fb-field__hint\">Tipo del risultato</label>\r\n <select\r\n class=\"fb-select\"\r\n [fbValue]=\"value()?.formulaDataType || ''\"\r\n [disabled]=\"disabled()\"\r\n (change)=\"onFormulaTypeChange($any($event.target).value)\"\r\n >\r\n @for (type of dataTypeOptions(); track type.value) {\r\n <option [value]=\"type.value\">{{ type.label }}</option>\r\n }\r\n </select>\r\n </div>\r\n <p class=\"fb-field__hint\">\r\n L\u2019espressione va al motore di regole: il backend non ne verifica la sintassi.\r\n </p>\r\n }\r\n\r\n @case ('empty') {\r\n <p class=\"fb-field__hint\">Nessun valore.</p>\r\n }\r\n }\r\n</div>\r\n", styles: [":host{display:block}.fb-value{display:flex;flex-direction:column;gap:4px}.fb-value__modes{display:flex;flex-wrap:wrap;gap:2px}.fb-value__mode{padding:2px 7px;border:1px solid var(--fb-border-subtle, #e6e9ee);border-radius:10px;background:transparent;color:var(--fb-text-muted, #667085);font:inherit;font-size:10px;cursor:pointer}.fb-value__mode:hover:not(:disabled){background:var(--fb-surface-alt, #f8f9fb)}.fb-value__mode--active{border-color:var(--fb-accent, #2f6feb);background:color-mix(in srgb,var(--fb-accent, #2f6feb) 10%,transparent);color:var(--fb-accent, #2f6feb);font-weight:600}.fb-value__mode--clear{margin-left:auto}.fb-value__mode:disabled{opacity:.5;cursor:not-allowed}\n"], dependencies: [{ kind: "component", type: ReferencePickerComponent, selector: "fb-reference-picker", inputs: ["value", "label", "placeholder", "disabled", "dataType", "isCollection", "objectType", "writableOnly", "elementsOnly"], outputs: ["valueChange"] }, { kind: "directive", type: SelectValueDirective, selector: "select[fbValue]", inputs: ["fbValue"] }], changeDetection: i0.ChangeDetectionStrategy.OnPush });
3272
3558
  }
3273
3559
  i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.27", ngImport: i0, type: ValueEditorComponent, decorators: [{
3274
3560
  type: Component,
3275
- args: [{ selector: 'fb-value-editor', standalone: true, imports: [ReferencePickerComponent, SelectValueDirective], changeDetection: ChangeDetectionStrategy.OnPush, template: "<div class=\"fb-value\">\r\n <div class=\"fb-value__modes\" role=\"group\" [attr.aria-label]=\"label() + ': modalita\u2019'\">\r\n <button\r\n type=\"button\"\r\n class=\"fb-value__mode\"\r\n [class.fb-value__mode--active]=\"mode() === 'reference'\"\r\n [disabled]=\"disabled()\"\r\n title=\"Riferimento a una risorsa o all\u2019output di un elemento\"\r\n (click)=\"setMode('reference')\"\r\n >\r\n Riferimento\r\n </button>\r\n @if (literalAllowed()) {\r\n <button\r\n type=\"button\"\r\n class=\"fb-value__mode\"\r\n [class.fb-value__mode--active]=\"mode() === 'literal'\"\r\n [disabled]=\"disabled()\"\r\n title=\"Valore letterale del tipo della destinazione\"\r\n (click)=\"setMode('literal')\"\r\n >\r\n Valore\r\n </button>\r\n }\r\n @if (allowFormula()) {\r\n <button\r\n type=\"button\"\r\n class=\"fb-value__mode\"\r\n [class.fb-value__mode--active]=\"mode() === 'formula'\"\r\n [disabled]=\"disabled()\"\r\n title=\"Espressione calcolata dal motore di regole\"\r\n (click)=\"setMode('formula')\"\r\n >\r\n Formula\r\n </button>\r\n }\r\n @if (globalConstants().length) {\r\n <button\r\n type=\"button\"\r\n class=\"fb-value__mode\"\r\n [class.fb-value__mode--active]=\"mode() === 'globalConstant'\"\r\n [disabled]=\"disabled()\"\r\n title=\"Costante globale\"\r\n (click)=\"setMode('globalConstant')\"\r\n >\r\n Costante globale\r\n </button>\r\n }\r\n @if (mode() !== 'empty') {\r\n <button\r\n type=\"button\"\r\n class=\"fb-value__mode fb-value__mode--clear\"\r\n [disabled]=\"disabled()\"\r\n title=\"Nessun valore\"\r\n (click)=\"setMode('empty')\"\r\n >\r\n \u00D7\r\n </button>\r\n }\r\n </div>\r\n\r\n @if (isAmbiguous()) {\r\n <!-- Piu' campi di valore insieme: il comportamento a runtime dipende dall'ordine di lettura. -->\r\n <p class=\"fb-callout fb-callout--warn\">\r\n Questo valore ha {{ filledFieldCount() }} campi valorizzati insieme: a runtime conta l\u2019ordine di lettura.\r\n <button type=\"button\" class=\"fb-btn fb-btn--ghost fb-btn--icon\" (click)=\"normalize()\">\r\n Tieni solo \u00AB{{ mode() === 'formula' ? 'formula' : mode() === 'literal' ? 'valore' : 'riferimento' }}\u00BB\r\n </button>\r\n </p>\r\n }\r\n\r\n @switch (mode()) {\r\n @case ('reference') {\r\n <fb-reference-picker\r\n [value]=\"value()?.elementReference\"\r\n [label]=\"label()\"\r\n [dataType]=\"dataType()\"\r\n [isCollection]=\"isCollection()\"\r\n [objectType]=\"objectType()\"\r\n [disabled]=\"disabled()\"\r\n (valueChange)=\"onReferenceChange($event)\"\r\n />\r\n }\r\n\r\n @case ('globalConstant') {\r\n <select\r\n class=\"fb-select\"\r\n [disabled]=\"disabled()\"\r\n [fbValue]=\"value()?.elementReference || ''\"\r\n (change)=\"onGlobalConstantChange($any($event.target).value)\"\r\n >\r\n @for (constant of globalConstants(); track constant) {\r\n <option [value]=\"constant\">{{ constant }}</option>\r\n }\r\n </select>\r\n }\r\n\r\n @case ('literal') {\r\n @if (dataType() === 'Boolean') {\r\n <label class=\"fb-check\">\r\n <input\r\n type=\"checkbox\"\r\n [checked]=\"booleanValue()\"\r\n [disabled]=\"disabled()\"\r\n (change)=\"onBooleanChange($any($event.target).checked)\"\r\n />\r\n {{ booleanValue() ? 'vero' : 'falso' }}\r\n </label>\r\n } @else {\r\n <input\r\n class=\"fb-input\"\r\n [type]=\"literalInputType()\"\r\n [value]=\"literalText()\"\r\n [disabled]=\"disabled()\"\r\n [attr.aria-label]=\"label()\"\r\n [placeholder]=\"dataType() === 'Enum' ? 'Nome del valore di enum' : ''\"\r\n (input)=\"onLiteralChange($any($event.target).value)\"\r\n />\r\n @if (numericScaleHint()) {\r\n <p class=\"fb-field__hint\">{{ numericScaleHint() }}</p>\r\n }\r\n @if (dataType() === 'Date') {\r\n <p class=\"fb-field__hint\">\r\n Data e ora insieme. Il backend non converte i fusi: scrivi il valore che il motore deve vedere.\r\n </p>\r\n }\r\n }\r\n }\r\n\r\n @case ('formula') {\r\n <textarea\r\n class=\"fb-textarea fb-input--mono\"\r\n [value]=\"value()?.formulaExpression || ''\"\r\n [disabled]=\"disabled()\"\r\n placeholder=\"Importo * 1.22\"\r\n [attr.aria-label]=\"label() + ': espressione'\"\r\n (input)=\"onFormulaChange($any($event.target).value)\"\r\n ></textarea>\r\n <div class=\"fb-field__row\">\r\n <label class=\"fb-field__hint\">Tipo del risultato</label>\r\n <select\r\n class=\"fb-select\"\r\n [fbValue]=\"value()?.formulaDataType || ''\"\r\n [disabled]=\"disabled()\"\r\n (change)=\"onFormulaTypeChange($any($event.target).value)\"\r\n >\r\n @for (type of dataTypeOptions(); track type.value) {\r\n <option [value]=\"type.value\">{{ type.label }}</option>\r\n }\r\n </select>\r\n </div>\r\n <p class=\"fb-field__hint\">\r\n L\u2019espressione va al motore di regole: il backend non ne verifica la sintassi.\r\n </p>\r\n }\r\n\r\n @case ('empty') {\r\n <p class=\"fb-field__hint\">Nessun valore.</p>\r\n }\r\n }\r\n</div>\r\n", styles: [":host{display:block}.fb-value{display:flex;flex-direction:column;gap:4px}.fb-value__modes{display:flex;flex-wrap:wrap;gap:2px}.fb-value__mode{padding:2px 7px;border:1px solid var(--fb-border-subtle, #e6e9ee);border-radius:10px;background:transparent;color:var(--fb-text-muted, #667085);font:inherit;font-size:10px;cursor:pointer}.fb-value__mode:hover:not(:disabled){background:var(--fb-surface-alt, #f8f9fb)}.fb-value__mode--active{border-color:var(--fb-accent, #2f6feb);background:color-mix(in srgb,var(--fb-accent, #2f6feb) 10%,transparent);color:var(--fb-accent, #2f6feb);font-weight:600}.fb-value__mode--clear{margin-left:auto}.fb-value__mode:disabled{opacity:.5;cursor:not-allowed}\n"] }]
3561
+ args: [{ selector: 'fb-value-editor', standalone: true, imports: [ReferencePickerComponent, SelectValueDirective], changeDetection: ChangeDetectionStrategy.OnPush, template: "<div class=\"fb-value\">\r\n <div class=\"fb-value__modes\" role=\"group\" [attr.aria-label]=\"label() + ': modalita\u2019'\">\r\n <button\r\n type=\"button\"\r\n class=\"fb-value__mode\"\r\n [class.fb-value__mode--active]=\"mode() === 'reference'\"\r\n [disabled]=\"disabled()\"\r\n title=\"Riferimento a una risorsa o all\u2019output di un elemento\"\r\n (click)=\"setMode('reference')\"\r\n >\r\n Riferimento\r\n </button>\r\n @if (literalAllowed()) {\r\n <button\r\n type=\"button\"\r\n class=\"fb-value__mode\"\r\n [class.fb-value__mode--active]=\"mode() === 'literal'\"\r\n [disabled]=\"disabled()\"\r\n title=\"Valore letterale del tipo della destinazione\"\r\n (click)=\"setMode('literal')\"\r\n >\r\n Valore\r\n </button>\r\n }\r\n @if (allowFormula()) {\r\n <button\r\n type=\"button\"\r\n class=\"fb-value__mode\"\r\n [class.fb-value__mode--active]=\"mode() === 'formula'\"\r\n [disabled]=\"disabled()\"\r\n title=\"Espressione calcolata dal motore di regole\"\r\n (click)=\"setMode('formula')\"\r\n >\r\n Formula\r\n </button>\r\n }\r\n @if (globalConstants().length) {\r\n <button\r\n type=\"button\"\r\n class=\"fb-value__mode\"\r\n [class.fb-value__mode--active]=\"mode() === 'globalConstant'\"\r\n [disabled]=\"disabled()\"\r\n title=\"Costante globale\"\r\n (click)=\"setMode('globalConstant')\"\r\n >\r\n Costante globale\r\n </button>\r\n }\r\n @if (mode() !== 'empty') {\r\n <button\r\n type=\"button\"\r\n class=\"fb-value__mode fb-value__mode--clear\"\r\n [disabled]=\"disabled()\"\r\n title=\"Nessun valore\"\r\n (click)=\"setMode('empty')\"\r\n >\r\n \u00D7\r\n </button>\r\n }\r\n </div>\r\n\r\n @if (isAmbiguous()) {\r\n <!-- Piu' campi di valore insieme: il comportamento a runtime dipende dall'ordine di lettura. -->\r\n <p class=\"fb-callout fb-callout--warn\">\r\n Questo valore ha {{ filledFieldCount() }} campi valorizzati insieme: a runtime conta l\u2019ordine di lettura.\r\n <button type=\"button\" class=\"fb-btn fb-btn--ghost fb-btn--icon\" (click)=\"normalize()\">\r\n Tieni solo \u00AB{{ mode() === 'formula' ? 'formula' : mode() === 'literal' ? 'valore' : 'riferimento' }}\u00BB\r\n </button>\r\n </p>\r\n }\r\n\r\n @switch (mode()) {\r\n @case ('reference') {\r\n <fb-reference-picker\r\n [value]=\"value()?.elementReference\"\r\n [label]=\"label()\"\r\n [dataType]=\"dataType()\"\r\n [isCollection]=\"isCollection()\"\r\n [objectType]=\"objectType()\"\r\n [disabled]=\"disabled()\"\r\n (valueChange)=\"onReferenceChange($event)\"\r\n />\r\n }\r\n\r\n @case ('globalConstant') {\r\n <select\r\n class=\"fb-select\"\r\n [disabled]=\"disabled()\"\r\n [fbValue]=\"value()?.elementReference || ''\"\r\n (change)=\"onGlobalConstantChange($any($event.target).value)\"\r\n >\r\n @for (constant of globalConstants(); track constant) {\r\n <option [value]=\"constant\">{{ constant }}</option>\r\n }\r\n </select>\r\n }\r\n\r\n @case ('literal') {\r\n @if (dataType() === 'Boolean') {\r\n <label class=\"fb-check\">\r\n <input\r\n type=\"checkbox\"\r\n [checked]=\"booleanValue()\"\r\n [disabled]=\"disabled()\"\r\n (change)=\"onBooleanChange($any($event.target).checked)\"\r\n />\r\n {{ booleanValue() ? 'vero' : 'falso' }}\r\n </label>\r\n } @else {\r\n <input\r\n class=\"fb-input\"\r\n [type]=\"literalInputType()\"\r\n [value]=\"literalText()\"\r\n [disabled]=\"disabled()\"\r\n [attr.aria-label]=\"label()\"\r\n [placeholder]=\"dataType() === 'Enum' ? 'Nome del valore di enum' : ''\"\r\n (input)=\"onLiteralChange($any($event.target).value)\"\r\n />\r\n @if (numericHint()) {\r\n <p class=\"fb-field__hint\">{{ numericHint() }}</p>\r\n }\r\n @if (dataType() === 'Date') {\r\n <!--\r\n Il fuso e' una scelta, non un dettaglio: `09:00Z` e `09:00` sono due istanti\r\n diversi, e il motore converte in UTC prima di ogni confronto (\u00A74.2).\r\n -->\r\n <div class=\"fb-value__modes\" role=\"group\" aria-label=\"Fuso del valore data\">\r\n <button\r\n type=\"button\"\r\n class=\"fb-value__mode\"\r\n [class.fb-value__mode--active]=\"!dateIsUtc()\"\r\n [disabled]=\"disabled()\"\r\n title=\"Interpretato nel fuso dell\u2019applicazione\"\r\n (click)=\"setDateZone(false)\"\r\n >\r\n Ora locale\r\n </button>\r\n <button\r\n type=\"button\"\r\n class=\"fb-value__mode\"\r\n [class.fb-value__mode--active]=\"dateIsUtc()\"\r\n [disabled]=\"disabled()\"\r\n title=\"Scrive il suffisso Z: l\u2019orario e\u2019 in UTC\"\r\n (click)=\"setDateZone(true)\"\r\n >\r\n UTC (Z)\r\n </button>\r\n </div>\r\n <p class=\"fb-field__hint\">\r\n Data e ora insieme.\r\n {{\r\n dateIsUtc()\r\n ? 'Con \u00ABUTC\u00BB l\u2019orario e\u2019 assoluto.'\r\n : 'Senza fuso l\u2019orario e\u2019 interpretato nel fuso dell\u2019applicazione, non in UTC.'\r\n }}\r\n Cambiare fuso riscrive l\u2019orario, non lo converte.\r\n </p>\r\n }\r\n }\r\n }\r\n\r\n @case ('formula') {\r\n <textarea\r\n class=\"fb-textarea fb-input--mono\"\r\n [value]=\"value()?.formulaExpression || ''\"\r\n [disabled]=\"disabled()\"\r\n placeholder=\"Importo * 1.22\"\r\n [attr.aria-label]=\"label() + ': espressione'\"\r\n (input)=\"onFormulaChange($any($event.target).value)\"\r\n ></textarea>\r\n <div class=\"fb-field__row\">\r\n <label class=\"fb-field__hint\">Tipo del risultato</label>\r\n <select\r\n class=\"fb-select\"\r\n [fbValue]=\"value()?.formulaDataType || ''\"\r\n [disabled]=\"disabled()\"\r\n (change)=\"onFormulaTypeChange($any($event.target).value)\"\r\n >\r\n @for (type of dataTypeOptions(); track type.value) {\r\n <option [value]=\"type.value\">{{ type.label }}</option>\r\n }\r\n </select>\r\n </div>\r\n <p class=\"fb-field__hint\">\r\n L\u2019espressione va al motore di regole: il backend non ne verifica la sintassi.\r\n </p>\r\n }\r\n\r\n @case ('empty') {\r\n <p class=\"fb-field__hint\">Nessun valore.</p>\r\n }\r\n }\r\n</div>\r\n", styles: [":host{display:block}.fb-value{display:flex;flex-direction:column;gap:4px}.fb-value__modes{display:flex;flex-wrap:wrap;gap:2px}.fb-value__mode{padding:2px 7px;border:1px solid var(--fb-border-subtle, #e6e9ee);border-radius:10px;background:transparent;color:var(--fb-text-muted, #667085);font:inherit;font-size:10px;cursor:pointer}.fb-value__mode:hover:not(:disabled){background:var(--fb-surface-alt, #f8f9fb)}.fb-value__mode--active{border-color:var(--fb-accent, #2f6feb);background:color-mix(in srgb,var(--fb-accent, #2f6feb) 10%,transparent);color:var(--fb-accent, #2f6feb);font-weight:600}.fb-value__mode--clear{margin-left:auto}.fb-value__mode:disabled{opacity:.5;cursor:not-allowed}\n"] }]
3276
3562
  }], propDecorators: { value: [{ type: i0.Input, args: [{ isSignal: true, alias: "value", required: false }] }], label: [{ type: i0.Input, args: [{ isSignal: true, alias: "label", required: false }] }], dataType: [{ type: i0.Input, args: [{ isSignal: true, alias: "dataType", required: false }] }], objectType: [{ type: i0.Input, args: [{ isSignal: true, alias: "objectType", required: false }] }], isCollection: [{ type: i0.Input, args: [{ isSignal: true, alias: "isCollection", required: false }] }], disabled: [{ type: i0.Input, args: [{ isSignal: true, alias: "disabled", required: false }] }], allowFormula: [{ type: i0.Input, args: [{ isSignal: true, alias: "allowFormula", required: false }] }], valueChange: [{ type: i0.Output, args: ["valueChange"] }] } });
3277
3563
 
3278
3564
  /**
3279
- * Editor delle condizioni — FRONTEND.md §4.3 e trappole §13.1, §13.4.
3565
+ * Editor delle condizioni — FRONTEND.md §4.3 e trappole §13.1, §13.4, §13.13, §13.14.
3280
3566
  *
3281
- * Due cose che questo componente fa deliberatamente, perche' un editor che non le fa
3567
+ * Quattro cose che questo componente fa deliberatamente, perche' un editor che non le fa
3282
3568
  * produce condizioni che fanno il contrario di quello che l'utente crede:
3283
3569
  *
3284
3570
  * 1. **Operatori unari.** Sette operatori non confrontano due valori: per loro
@@ -3290,21 +3576,117 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.27", ngImpo
3290
3576
  * perche' gli indici sono 1-based e scalano. La riscrittura sta in
3291
3577
  * {@link removeCondition} / {@link moveCondition}.
3292
3578
  *
3579
+ * 3. **Testo e numero non si confrontano** (`CONDITION_TYPE_MISMATCH`, errore che blocca
3580
+ * l'attivazione). Il tipo del lato sinistro arriva da `POST /flows/references` e guida il
3581
+ * secondo operando: il letterale finisce nel campo giusto e una risorsa incompatibile e'
3582
+ * segnalata. Il backend segnala solo quando conosce **entrambi** i tipi, e su un campo di
3583
+ * un record non li conosce: fermare qui e' il solo modo di fermare in tempo.
3584
+ *
3585
+ * 4. **`None` non e' un operatore, e' un "da completare"**: la bozza si salva, l'attivazione
3586
+ * no. La condizione e' evidenziata come incompleta, non presentata come valida.
3587
+ *
3293
3588
  * Inoltre: `WasVisited` e `HasError` vogliono a sinistra il nome di un **node**, non di una
3294
3589
  * risorsa (`appliesToElements`), e il selettore cambia di conseguenza.
3295
3590
  */
3296
3591
  class ConditionEditorComponent {
3297
3592
  dictionaries = inject(FlowDictionaryStore);
3593
+ api = inject(FlowBuilderApi);
3594
+ store = inject(FlowDocumentStore);
3298
3595
  /** Il contenitore: porta `conditions`, `conditionLogic` e, se serve, `formula`. */
3299
3596
  holder = input.required(...(ngDevMode ? [{ debugName: "holder" }] : []));
3300
3597
  title = input('Condizioni', ...(ngDevMode ? [{ debugName: "title" }] : []));
3301
3598
  /** `false` dove il modello non prevede la modalita' formula sull'elemento. */
3302
3599
  allowFormula = input(true, ...(ngDevMode ? [{ debugName: "allowFormula" }] : []));
3600
+ /**
3601
+ * `false` dove il modello porta **solo** la lista di condizioni, senza un campo per la
3602
+ * logica: gli `entryConditions` / `exitConditions` di uno step di orchestrazione (§5.13).
3603
+ * Mostrare i pulsanti della logica lì scriverebbe un campo che il contratto non prevede.
3604
+ */
3605
+ allowLogic = input(true, ...(ngDevMode ? [{ debugName: "allowLogic" }] : []));
3303
3606
  /** Path per ancorare i rilievi di validazione, es. `rules[Approvato]`. */
3304
3607
  issuePath = input('', ...(ngDevMode ? [{ debugName: "issuePath" }] : []));
3305
3608
  /** Emesso a ogni modifica: il chiamante applica la mutazione allo store. */
3306
3609
  changed = output();
3307
3610
  conditions = computed(() => this.holder().conditions ?? [], ...(ngDevMode ? [{ debugName: "conditions" }] : []));
3611
+ /**
3612
+ * I riferimenti del documento, per conoscere il tipo del lato sinistro. Non e' un doppione
3613
+ * del picker: serve **il tipo**, che e' l'unica cosa che permette di impedire il confronto
3614
+ * fra testo e numero prima che lo dica la validazione (§4.3).
3615
+ */
3616
+ references = signal([], ...(ngDevMode ? [{ debugName: "references" }] : []));
3617
+ constructor() {
3618
+ effect(() => {
3619
+ const definition = this.store.document();
3620
+ // Senza condizioni non serve conoscere nessun tipo: in un form con molti blocchi di
3621
+ // condizioni vuoti (uno stage con dieci step) risparmia altrettante richieste.
3622
+ if (!this.conditions().length) {
3623
+ return;
3624
+ }
3625
+ void this.api
3626
+ .getReferences({ definition })
3627
+ .then((list) => this.references.set(list ?? []))
3628
+ // Senza l'elenco non si segnala nulla: non sapere non e' sapere che e' sbagliato.
3629
+ .catch(() => this.references.set([]));
3630
+ });
3631
+ }
3632
+ /** Il tipo di un riferimento, `undefined` se navigato o ignoto: lì non si segnala (§4.3). */
3633
+ typeOfReference(reference) {
3634
+ if (!reference) {
3635
+ return undefined;
3636
+ }
3637
+ return this.references().find((entry) => entry.name === reference)?.dataType ?? undefined;
3638
+ }
3639
+ /** Il tipo del lato sinistro: guida il secondo operando. */
3640
+ leftDataType(condition) {
3641
+ return this.typeOfReference(condition.leftValueReference);
3642
+ }
3643
+ /** Il tipo del lato destro, dedotto dal campo valorizzato o dal riferimento scelto. */
3644
+ rightDataType(condition) {
3645
+ const value = condition.rightValue;
3646
+ if (!value) {
3647
+ return undefined;
3648
+ }
3649
+ if (value.formulaExpression !== undefined) {
3650
+ return value.formulaDataType;
3651
+ }
3652
+ if (value.elementReference !== undefined) {
3653
+ return this.typeOfReference(value.elementReference);
3654
+ }
3655
+ if (value.stringValue !== undefined) {
3656
+ return 'String';
3657
+ }
3658
+ if (value.integerValue !== undefined) {
3659
+ return 'Integer';
3660
+ }
3661
+ if (value.numberValue !== undefined) {
3662
+ return 'Number';
3663
+ }
3664
+ if (value.dateValue !== undefined) {
3665
+ return 'Date';
3666
+ }
3667
+ if (value.booleanValue !== undefined) {
3668
+ return 'Boolean';
3669
+ }
3670
+ if (value.enumValue !== undefined) {
3671
+ return 'Enum';
3672
+ }
3673
+ return undefined;
3674
+ }
3675
+ /**
3676
+ * `true` quando i due lati sono incompatibili: e' `CONDITION_TYPE_MISMATCH`, un errore che
3677
+ * blocca l'attivazione. L'unica coppia vietata e' testo con numero (§4.3).
3678
+ */
3679
+ hasTypeMismatch(condition) {
3680
+ if (!isTypeCheckedOperator(condition.operator, this.isUnary(condition))) {
3681
+ return false;
3682
+ }
3683
+ return !areTypesComparable(this.leftDataType(condition), this.rightDataType(condition));
3684
+ }
3685
+ typeMismatchMessage(condition) {
3686
+ const left = this.leftDataType(condition) ?? '?';
3687
+ const right = this.rightDataType(condition) ?? '?';
3688
+ return `Un valore ${left} non si confronta con un valore ${right}: e’ CONDITION_TYPE_MISMATCH e blocca l’attivazione.`;
3689
+ }
3308
3690
  logicMode = computed(() => {
3309
3691
  const logic = this.holder().conditionLogic;
3310
3692
  if (!logic) {
@@ -3331,7 +3713,19 @@ class ConditionEditorComponent {
3331
3713
  }, ...(ngDevMode ? [{ debugName: "customLogicError" }] : []));
3332
3714
  /** Con `Formula` il campo `conditions` viene **ignorato** dal motore (§4.3). */
3333
3715
  conditionsIgnored = computed(() => this.logicMode() === 'formula', ...(ngDevMode ? [{ debugName: "conditionsIgnored" }] : []));
3334
- operators = computed(() => this.dictionaries.comparisonOperators(), ...(ngDevMode ? [{ debugName: "operators" }] : []));
3716
+ /**
3717
+ * Gli operatori applicabili al tipo del lato sinistro: `appliesTo` del dizionario, vuoto =
3718
+ * qualunque tipo (§4.3). L'operatore già scritto si tiene comunque nell'elenco, altrimenti
3719
+ * il `select` resterebbe vuoto pur avendo un valore.
3720
+ */
3721
+ operatorsFor(condition) {
3722
+ const applicable = this.dictionaries.operatorsFor(this.leftDataType(condition));
3723
+ if (!condition.operator || applicable.some((entry) => entry.value === condition.operator)) {
3724
+ return applicable;
3725
+ }
3726
+ const current = this.dictionaries.operator(condition.operator);
3727
+ return current ? [...applicable, current] : applicable;
3728
+ }
3335
3729
  isUnary(condition) {
3336
3730
  return this.dictionaries.isUnaryOperator(condition.operator);
3337
3731
  }
@@ -3360,10 +3754,15 @@ class ConditionEditorComponent {
3360
3754
  operatorDescription(operator) {
3361
3755
  return this.dictionaries.operator(operator)?.description ?? null;
3362
3756
  }
3363
- /** `None` consente di salvare una condizione incompleta: a runtime vale sempre falso. */
3757
+ /**
3758
+ * `None` e' uno stato dell'editor, non un operatore: la **bozza si salva, l'attivazione no**
3759
+ * (`CONDITION_INCOMPLETE`, errore). A runtime vale sempre falso (§4.3, §13.14).
3760
+ */
3364
3761
  isPlaceholderOperator(condition) {
3365
3762
  return condition.operator === 'None';
3366
3763
  }
3764
+ /** Quante condizioni sono da completare: si dice in testa, non solo riga per riga. */
3765
+ incompleteCount = computed(() => this.conditions().filter((condition) => condition.operator === 'None').length, ...(ngDevMode ? [{ debugName: "incompleteCount" }] : []));
3367
3766
  // -------------------------------------------------------------------------
3368
3767
  // Mutazioni
3369
3768
  // -------------------------------------------------------------------------
@@ -3466,12 +3865,12 @@ class ConditionEditorComponent {
3466
3865
  });
3467
3866
  }
3468
3867
  static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.3.27", ngImport: i0, type: ConditionEditorComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
3469
- static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "20.3.27", 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 }, 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 <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\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 <textarea\r\n class=\"fb-textarea fb-input--mono\"\r\n [value]=\"holder().formula || ''\"\r\n placeholder=\"AND(Esito = 'KO', Importo > 1000)\"\r\n (input)=\"setFormula($any($event.target).value)\"\r\n ></textarea>\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 </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 <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 class=\"fb-cond__placeholder\" title=\"A runtime vale sempre falso: e\u2019 un segnaposto\">\r\n incompleta\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 <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 [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\">\r\n <label class=\"fb-field__label\">Operatore</label>\r\n <select\r\n class=\"fb-select\"\r\n [fbValue]=\"condition.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 @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 <fb-value-editor\r\n [value]=\"condition.rightValue\"\r\n label=\"Valore di confronto\"\r\n (valueChange)=\"setRightValue($index, $event)\"\r\n />\r\n </div>\r\n }\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{display:flex;flex-wrap:wrap;gap:3px;margin-top:3px}.fb-cond__mode{padding:3px 8px;border:1px solid var(--fb-border-subtle, #e6e9ee);border-radius:12px;background:transparent;color:var(--fb-text-muted, #667085);font:inherit;font-size:11px;cursor:pointer}.fb-cond__mode:hover{background:var(--fb-surface-alt, #f8f9fb)}.fb-cond__mode--active{border-color:var(--fb-accent, #2f6feb);background:color-mix(in srgb,var(--fb-accent, #2f6feb) 10%,transparent);color:var(--fb-accent, #2f6feb);font-weight:600}.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: ReferencePickerComponent, selector: "fb-reference-picker", inputs: ["value", "label", "placeholder", "disabled", "dataType", "isCollection", "objectType", "writableOnly", "allowStageTargets", "elementsOnly"], outputs: ["valueChange"] }, { kind: "component", type: ValueEditorComponent, selector: "fb-value-editor", inputs: ["value", "label", "dataType", "objectType", "isCollection", "disabled", "allowFormula"], outputs: ["valueChange"] }, { kind: "directive", type: SelectValueDirective, selector: "select[fbValue]", inputs: ["fbValue"] }], changeDetection: i0.ChangeDetectionStrategy.OnPush });
3868
+ static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "20.3.27", 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 }, 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 <textarea\r\n class=\"fb-textarea fb-input--mono\"\r\n [value]=\"holder().formula || ''\"\r\n placeholder=\"AND(Esito = 'KO', Importo > 1000)\"\r\n (input)=\"setFormula($any($event.target).value)\"\r\n ></textarea>\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 </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 <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 [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\">\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 (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 } @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{display:flex;flex-wrap:wrap;gap:3px;margin-top:3px}.fb-cond__mode{padding:3px 8px;border:1px solid var(--fb-border-subtle, #e6e9ee);border-radius:12px;background:transparent;color:var(--fb-text-muted, #667085);font:inherit;font-size:11px;cursor:pointer}.fb-cond__mode:hover{background:var(--fb-surface-alt, #f8f9fb)}.fb-cond__mode--active{border-color:var(--fb-accent, #2f6feb);background:color-mix(in srgb,var(--fb-accent, #2f6feb) 10%,transparent);color:var(--fb-accent, #2f6feb);font-weight:600}.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: ReferencePickerComponent, selector: "fb-reference-picker", inputs: ["value", "label", "placeholder", "disabled", "dataType", "isCollection", "objectType", "writableOnly", "elementsOnly"], outputs: ["valueChange"] }, { kind: "component", type: ValueEditorComponent, selector: "fb-value-editor", inputs: ["value", "label", "dataType", "objectType", "isCollection", "disabled", "allowFormula"], outputs: ["valueChange"] }, { kind: "directive", type: SelectValueDirective, selector: "select[fbValue]", inputs: ["fbValue"] }], changeDetection: i0.ChangeDetectionStrategy.OnPush });
3470
3869
  }
3471
3870
  i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.27", ngImport: i0, type: ConditionEditorComponent, decorators: [{
3472
3871
  type: Component,
3473
- args: [{ selector: 'fb-condition-editor', standalone: true, imports: [ReferencePickerComponent, ValueEditorComponent, SelectValueDirective], changeDetection: ChangeDetectionStrategy.OnPush, template: "<fieldset class=\"fb-section\">\r\n <legend class=\"fb-section__title\">{{ title() }}</legend>\r\n\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\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 <textarea\r\n class=\"fb-textarea fb-input--mono\"\r\n [value]=\"holder().formula || ''\"\r\n placeholder=\"AND(Esito = 'KO', Importo > 1000)\"\r\n (input)=\"setFormula($any($event.target).value)\"\r\n ></textarea>\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 </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 <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 class=\"fb-cond__placeholder\" title=\"A runtime vale sempre falso: e\u2019 un segnaposto\">\r\n incompleta\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 <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 [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\">\r\n <label class=\"fb-field__label\">Operatore</label>\r\n <select\r\n class=\"fb-select\"\r\n [fbValue]=\"condition.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 @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 <fb-value-editor\r\n [value]=\"condition.rightValue\"\r\n label=\"Valore di confronto\"\r\n (valueChange)=\"setRightValue($index, $event)\"\r\n />\r\n </div>\r\n }\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{display:flex;flex-wrap:wrap;gap:3px;margin-top:3px}.fb-cond__mode{padding:3px 8px;border:1px solid var(--fb-border-subtle, #e6e9ee);border-radius:12px;background:transparent;color:var(--fb-text-muted, #667085);font:inherit;font-size:11px;cursor:pointer}.fb-cond__mode:hover{background:var(--fb-surface-alt, #f8f9fb)}.fb-cond__mode--active{border-color:var(--fb-accent, #2f6feb);background:color-mix(in srgb,var(--fb-accent, #2f6feb) 10%,transparent);color:var(--fb-accent, #2f6feb);font-weight:600}.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"] }]
3474
- }], 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 }] }], issuePath: [{ type: i0.Input, args: [{ isSignal: true, alias: "issuePath", required: false }] }], changed: [{ type: i0.Output, args: ["changed"] }] } });
3872
+ args: [{ selector: 'fb-condition-editor', standalone: true, imports: [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 <textarea\r\n class=\"fb-textarea fb-input--mono\"\r\n [value]=\"holder().formula || ''\"\r\n placeholder=\"AND(Esito = 'KO', Importo > 1000)\"\r\n (input)=\"setFormula($any($event.target).value)\"\r\n ></textarea>\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 </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 <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 [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\">\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 (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 } @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{display:flex;flex-wrap:wrap;gap:3px;margin-top:3px}.fb-cond__mode{padding:3px 8px;border:1px solid var(--fb-border-subtle, #e6e9ee);border-radius:12px;background:transparent;color:var(--fb-text-muted, #667085);font:inherit;font-size:11px;cursor:pointer}.fb-cond__mode:hover{background:var(--fb-surface-alt, #f8f9fb)}.fb-cond__mode--active{border-color:var(--fb-accent, #2f6feb);background:color-mix(in srgb,var(--fb-accent, #2f6feb) 10%,transparent);color:var(--fb-accent, #2f6feb);font-weight:600}.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"] }]
3873
+ }], 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 }] }], 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"] }] } });
3475
3874
 
3476
3875
  /**
3477
3876
  * Editor dei filtri sui record — FRONTEND.md §4.4.
@@ -3776,7 +4175,7 @@ class ParameterEditorComponent {
3776
4175
  });
3777
4176
  }
3778
4177
  static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.3.27", ngImport: i0, type: ParameterEditorComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
3779
- static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "20.3.27", type: ParameterEditorComponent, isStandalone: true, selector: "fb-parameter-editor", inputs: { holder: { classPropertyName: "holder", publicName: "holder", isSignal: true, isRequired: true, transformFunction: null }, catalogParameters: { classPropertyName: "catalogParameters", publicName: "catalogParameters", isSignal: true, isRequired: false, transformFunction: null }, inputTitle: { classPropertyName: "inputTitle", publicName: "inputTitle", isSignal: true, isRequired: false, transformFunction: null }, outputTitle: { classPropertyName: "outputTitle", publicName: "outputTitle", isSignal: true, isRequired: false, transformFunction: null }, showInputs: { classPropertyName: "showInputs", publicName: "showInputs", isSignal: true, isRequired: false, transformFunction: null }, showOutputs: { classPropertyName: "showOutputs", publicName: "showOutputs", isSignal: true, isRequired: false, transformFunction: null }, outputsDisabledReason: { classPropertyName: "outputsDisabledReason", publicName: "outputsDisabledReason", isSignal: true, isRequired: false, transformFunction: null } }, outputs: { changed: "changed" }, ngImport: i0, template: "@if (showInputs()) {\r\n <fieldset class=\"fb-section\">\r\n <legend class=\"fb-section__title\">{{ inputTitle() }}</legend>\r\n\r\n @if (missingRequired().length) {\r\n <p class=\"fb-callout fb-callout--warn\">\r\n Parametri obbligatori non ancora impostati:\r\n @for (parameter of missingRequired(); track parameter.name) {\r\n <button type=\"button\" class=\"fb-btn fb-btn--ghost fb-btn--icon\" (click)=\"addInput(parameter.name)\">\r\n + {{ parameter.name }}\r\n </button>\r\n }\r\n </p>\r\n }\r\n\r\n <div class=\"fb-list\">\r\n @for (parameter of inputs(); track $index) {\r\n <div class=\"fb-list__item\">\r\n <div class=\"fb-list__header\">\r\n @if (hasCatalog()) {\r\n <select\r\n class=\"fb-select\"\r\n [fbValue]=\"parameter.name || ''\"\r\n aria-label=\"Nome del parametro\"\r\n (change)=\"setInputName($index, $any($event.target).value)\"\r\n >\r\n <option value=\"\">\u2014 scegli \u2014</option>\r\n @for (candidate of inputCatalog(); track candidate.name) {\r\n <option [value]=\"candidate.name\">\r\n {{ candidate.label || candidate.name }}{{ candidate.isRequired ? ' *' : '' }}\r\n </option>\r\n }\r\n @if (unknownInput(parameter)) {\r\n <option [value]=\"parameter.name\">{{ parameter.name }} (non nel catalogo)</option>\r\n }\r\n </select>\r\n } @else {\r\n <input\r\n class=\"fb-input\"\r\n [value]=\"parameter.name || ''\"\r\n placeholder=\"Nome del parametro\"\r\n aria-label=\"Nome del parametro\"\r\n (input)=\"setInputName($index, $any($event.target).value)\"\r\n />\r\n }\r\n <button\r\n type=\"button\"\r\n class=\"fb-btn fb-btn--ghost fb-btn--icon\"\r\n aria-label=\"Rimuovi il parametro\"\r\n (click)=\"removeInput($index)\"\r\n >\r\n \u00D7\r\n </button>\r\n </div>\r\n\r\n @if (unknownInput(parameter)) {\r\n <p class=\"fb-field__hint fb-field__hint--warn\">\r\n Il catalogo non dichiara questo parametro: la validazione lo segnalerebbe come errore.\r\n </p>\r\n }\r\n @if (describe(parameter.name)?.description) {\r\n <p class=\"fb-field__hint\">{{ describe(parameter.name)?.description }}</p>\r\n }\r\n\r\n <fb-value-editor\r\n [value]=\"parameter.value\"\r\n [dataType]=\"describe(parameter.name)?.dataType\"\r\n [objectType]=\"describe(parameter.name)?.objectType || undefined\"\r\n [isCollection]=\"describe(parameter.name)?.isCollection\"\r\n label=\"Valore del parametro\"\r\n (valueChange)=\"setInputValue($index, $event)\"\r\n />\r\n </div>\r\n } @empty {\r\n <p class=\"fb-empty\">Nessun parametro di ingresso.</p>\r\n }\r\n </div>\r\n\r\n <button type=\"button\" class=\"fb-btn\" (click)=\"addInput()\">Aggiungi parametro</button>\r\n </fieldset>\r\n}\r\n\r\n@if (showOutputs()) {\r\n <fieldset class=\"fb-section\">\r\n <legend class=\"fb-section__title\">{{ outputTitle() }}</legend>\r\n\r\n @if (outputsDisabledReason()) {\r\n <p class=\"fb-callout\">{{ outputsDisabledReason() }}</p>\r\n }\r\n\r\n <div class=\"fb-list\">\r\n @for (parameter of outputs(); track $index) {\r\n <div class=\"fb-list__item\">\r\n <div class=\"fb-list__header\">\r\n @if (hasCatalog()) {\r\n <select\r\n class=\"fb-select\"\r\n [fbValue]=\"parameter.name || ''\"\r\n aria-label=\"Nome del parametro di uscita\"\r\n (change)=\"setOutputName($index, $any($event.target).value)\"\r\n >\r\n <option value=\"\">\u2014 scegli \u2014</option>\r\n @for (candidate of outputCatalog(); track candidate.name) {\r\n <option [value]=\"candidate.name\">{{ candidate.label || candidate.name }}</option>\r\n }\r\n @if (unknownOutput(parameter)) {\r\n <option [value]=\"parameter.name\">{{ parameter.name }} (non nel catalogo)</option>\r\n }\r\n </select>\r\n } @else {\r\n <input\r\n class=\"fb-input\"\r\n [value]=\"parameter.name || ''\"\r\n placeholder=\"Nome del parametro\"\r\n aria-label=\"Nome del parametro di uscita\"\r\n (input)=\"setOutputName($index, $any($event.target).value)\"\r\n />\r\n }\r\n <button\r\n type=\"button\"\r\n class=\"fb-btn fb-btn--ghost fb-btn--icon\"\r\n aria-label=\"Rimuovi il parametro\"\r\n (click)=\"removeOutput($index)\"\r\n >\r\n \u00D7\r\n </button>\r\n </div>\r\n\r\n @if (unknownOutput(parameter)) {\r\n <p class=\"fb-field__hint fb-field__hint--warn\">\r\n Il catalogo non dichiara questo parametro di uscita.\r\n </p>\r\n }\r\n\r\n <div class=\"fb-field\">\r\n <label class=\"fb-field__label\">Assegna a</label>\r\n <!-- Solo variabili: assegnare a una costante o a una formula e' un errore. -->\r\n <fb-reference-picker\r\n [value]=\"parameter.assignToReference\"\r\n [writableOnly]=\"true\"\r\n [dataType]=\"describe(parameter.name)?.dataType\"\r\n [isCollection]=\"describe(parameter.name)?.isCollection\"\r\n placeholder=\"Scegli una variabile\"\r\n (valueChange)=\"setOutputTarget($index, $event)\"\r\n />\r\n </div>\r\n </div>\r\n } @empty {\r\n <p class=\"fb-empty\">Nessun parametro di uscita.</p>\r\n }\r\n </div>\r\n\r\n <button type=\"button\" class=\"fb-btn\" (click)=\"addOutput()\">Aggiungi parametro</button>\r\n </fieldset>\r\n}\r\n", styles: [":host{display:block}.fb-list__header .fb-select,.fb-list__header .fb-input{flex:1;min-width:0}\n"], dependencies: [{ kind: "component", type: ReferencePickerComponent, selector: "fb-reference-picker", inputs: ["value", "label", "placeholder", "disabled", "dataType", "isCollection", "objectType", "writableOnly", "allowStageTargets", "elementsOnly"], outputs: ["valueChange"] }, { kind: "component", type: ValueEditorComponent, selector: "fb-value-editor", inputs: ["value", "label", "dataType", "objectType", "isCollection", "disabled", "allowFormula"], outputs: ["valueChange"] }, { kind: "directive", type: SelectValueDirective, selector: "select[fbValue]", inputs: ["fbValue"] }], changeDetection: i0.ChangeDetectionStrategy.OnPush });
4178
+ static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "20.3.27", type: ParameterEditorComponent, isStandalone: true, selector: "fb-parameter-editor", inputs: { holder: { classPropertyName: "holder", publicName: "holder", isSignal: true, isRequired: true, transformFunction: null }, catalogParameters: { classPropertyName: "catalogParameters", publicName: "catalogParameters", isSignal: true, isRequired: false, transformFunction: null }, inputTitle: { classPropertyName: "inputTitle", publicName: "inputTitle", isSignal: true, isRequired: false, transformFunction: null }, outputTitle: { classPropertyName: "outputTitle", publicName: "outputTitle", isSignal: true, isRequired: false, transformFunction: null }, showInputs: { classPropertyName: "showInputs", publicName: "showInputs", isSignal: true, isRequired: false, transformFunction: null }, showOutputs: { classPropertyName: "showOutputs", publicName: "showOutputs", isSignal: true, isRequired: false, transformFunction: null }, outputsDisabledReason: { classPropertyName: "outputsDisabledReason", publicName: "outputsDisabledReason", isSignal: true, isRequired: false, transformFunction: null } }, outputs: { changed: "changed" }, ngImport: i0, template: "@if (showInputs()) {\r\n <fieldset class=\"fb-section\">\r\n <legend class=\"fb-section__title\">{{ inputTitle() }}</legend>\r\n\r\n @if (missingRequired().length) {\r\n <p class=\"fb-callout fb-callout--warn\">\r\n Parametri obbligatori non ancora impostati:\r\n @for (parameter of missingRequired(); track parameter.name) {\r\n <button type=\"button\" class=\"fb-btn fb-btn--ghost fb-btn--icon\" (click)=\"addInput(parameter.name)\">\r\n + {{ parameter.name }}\r\n </button>\r\n }\r\n </p>\r\n }\r\n\r\n <div class=\"fb-list\">\r\n @for (parameter of inputs(); track $index) {\r\n <div class=\"fb-list__item\">\r\n <div class=\"fb-list__header\">\r\n @if (hasCatalog()) {\r\n <select\r\n class=\"fb-select\"\r\n [fbValue]=\"parameter.name || ''\"\r\n aria-label=\"Nome del parametro\"\r\n (change)=\"setInputName($index, $any($event.target).value)\"\r\n >\r\n <option value=\"\">\u2014 scegli \u2014</option>\r\n @for (candidate of inputCatalog(); track candidate.name) {\r\n <option [value]=\"candidate.name\">\r\n {{ candidate.label || candidate.name }}{{ candidate.isRequired ? ' *' : '' }}\r\n </option>\r\n }\r\n @if (unknownInput(parameter)) {\r\n <option [value]=\"parameter.name\">{{ parameter.name }} (non nel catalogo)</option>\r\n }\r\n </select>\r\n } @else {\r\n <input\r\n class=\"fb-input\"\r\n [value]=\"parameter.name || ''\"\r\n placeholder=\"Nome del parametro\"\r\n aria-label=\"Nome del parametro\"\r\n (input)=\"setInputName($index, $any($event.target).value)\"\r\n />\r\n }\r\n <button\r\n type=\"button\"\r\n class=\"fb-btn fb-btn--ghost fb-btn--icon\"\r\n aria-label=\"Rimuovi il parametro\"\r\n (click)=\"removeInput($index)\"\r\n >\r\n \u00D7\r\n </button>\r\n </div>\r\n\r\n @if (unknownInput(parameter)) {\r\n <p class=\"fb-field__hint fb-field__hint--warn\">\r\n Il catalogo non dichiara questo parametro: la validazione lo segnalerebbe come errore.\r\n </p>\r\n }\r\n @if (describe(parameter.name)?.description) {\r\n <p class=\"fb-field__hint\">{{ describe(parameter.name)?.description }}</p>\r\n }\r\n\r\n <fb-value-editor\r\n [value]=\"parameter.value\"\r\n [dataType]=\"describe(parameter.name)?.dataType\"\r\n [objectType]=\"describe(parameter.name)?.objectType || undefined\"\r\n [isCollection]=\"describe(parameter.name)?.isCollection\"\r\n label=\"Valore del parametro\"\r\n (valueChange)=\"setInputValue($index, $event)\"\r\n />\r\n </div>\r\n } @empty {\r\n <p class=\"fb-empty\">Nessun parametro di ingresso.</p>\r\n }\r\n </div>\r\n\r\n <button type=\"button\" class=\"fb-btn\" (click)=\"addInput()\">Aggiungi parametro</button>\r\n </fieldset>\r\n}\r\n\r\n@if (showOutputs()) {\r\n <fieldset class=\"fb-section\">\r\n <legend class=\"fb-section__title\">{{ outputTitle() }}</legend>\r\n\r\n @if (outputsDisabledReason()) {\r\n <p class=\"fb-callout\">{{ outputsDisabledReason() }}</p>\r\n }\r\n\r\n <div class=\"fb-list\">\r\n @for (parameter of outputs(); track $index) {\r\n <div class=\"fb-list__item\">\r\n <div class=\"fb-list__header\">\r\n @if (hasCatalog()) {\r\n <select\r\n class=\"fb-select\"\r\n [fbValue]=\"parameter.name || ''\"\r\n aria-label=\"Nome del parametro di uscita\"\r\n (change)=\"setOutputName($index, $any($event.target).value)\"\r\n >\r\n <option value=\"\">\u2014 scegli \u2014</option>\r\n @for (candidate of outputCatalog(); track candidate.name) {\r\n <option [value]=\"candidate.name\">{{ candidate.label || candidate.name }}</option>\r\n }\r\n @if (unknownOutput(parameter)) {\r\n <option [value]=\"parameter.name\">{{ parameter.name }} (non nel catalogo)</option>\r\n }\r\n </select>\r\n } @else {\r\n <input\r\n class=\"fb-input\"\r\n [value]=\"parameter.name || ''\"\r\n placeholder=\"Nome del parametro\"\r\n aria-label=\"Nome del parametro di uscita\"\r\n (input)=\"setOutputName($index, $any($event.target).value)\"\r\n />\r\n }\r\n <button\r\n type=\"button\"\r\n class=\"fb-btn fb-btn--ghost fb-btn--icon\"\r\n aria-label=\"Rimuovi il parametro\"\r\n (click)=\"removeOutput($index)\"\r\n >\r\n \u00D7\r\n </button>\r\n </div>\r\n\r\n @if (unknownOutput(parameter)) {\r\n <p class=\"fb-field__hint fb-field__hint--warn\">\r\n Il catalogo non dichiara questo parametro di uscita.\r\n </p>\r\n }\r\n\r\n <div class=\"fb-field\">\r\n <label class=\"fb-field__label\">Assegna a</label>\r\n <!-- Solo variabili: assegnare a una costante o a una formula e' un errore. -->\r\n <fb-reference-picker\r\n [value]=\"parameter.assignToReference\"\r\n [writableOnly]=\"true\"\r\n [dataType]=\"describe(parameter.name)?.dataType\"\r\n [isCollection]=\"describe(parameter.name)?.isCollection\"\r\n placeholder=\"Scegli una variabile\"\r\n (valueChange)=\"setOutputTarget($index, $event)\"\r\n />\r\n </div>\r\n </div>\r\n } @empty {\r\n <p class=\"fb-empty\">Nessun parametro di uscita.</p>\r\n }\r\n </div>\r\n\r\n <button type=\"button\" class=\"fb-btn\" (click)=\"addOutput()\">Aggiungi parametro</button>\r\n </fieldset>\r\n}\r\n", styles: [":host{display:block}.fb-list__header .fb-select,.fb-list__header .fb-input{flex:1;min-width:0}\n"], dependencies: [{ kind: "component", type: ReferencePickerComponent, selector: "fb-reference-picker", inputs: ["value", "label", "placeholder", "disabled", "dataType", "isCollection", "objectType", "writableOnly", "elementsOnly"], outputs: ["valueChange"] }, { kind: "component", type: ValueEditorComponent, selector: "fb-value-editor", inputs: ["value", "label", "dataType", "objectType", "isCollection", "disabled", "allowFormula"], outputs: ["valueChange"] }, { kind: "directive", type: SelectValueDirective, selector: "select[fbValue]", inputs: ["fbValue"] }], changeDetection: i0.ChangeDetectionStrategy.OnPush });
3780
4179
  }
3781
4180
  i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.27", ngImport: i0, type: ParameterEditorComponent, decorators: [{
3782
4181
  type: Component,
@@ -3863,11 +4262,11 @@ class FieldAssignmentEditorComponent {
3863
4262
  return !this.fields().some((field) => field.name === assignment.field);
3864
4263
  }
3865
4264
  static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.3.27", ngImport: i0, type: FieldAssignmentEditorComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
3866
- static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "20.3.27", type: FieldAssignmentEditorComponent, isStandalone: true, selector: "fb-field-assignment-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 }, disabledReason: { classPropertyName: "disabledReason", publicName: "disabledReason", isSignal: true, isRequired: false, transformFunction: null } }, outputs: { changed: "changed" }, ngImport: i0, template: "<fieldset class=\"fb-section\">\n <legend class=\"fb-section__title\">{{ title() }}</legend>\n\n @if (disabledReason()) {\n <p class=\"fb-callout\">{{ disabledReason() }}</p>\n }\n @if (!object()) {\n <p class=\"fb-field__hint\">Scegli prima un oggetto per poterne valorizzare i campi.</p>\n }\n @if (missingRequired().length) {\n <p class=\"fb-callout fb-callout--warn\">\n Campi obbligatori non valorizzati:\n @for (field of missingRequired(); track field.name) {\n <button type=\"button\" class=\"fb-btn fb-btn--ghost fb-btn--icon\" (click)=\"add(field.name)\">\n + {{ field.label || field.name }}\n </button>\n }\n </p>\n }\n\n <div class=\"fb-list\">\n @for (assignment of assignments(); track $index) {\n <div class=\"fb-list__item\">\n <div class=\"fb-list__header\">\n @if (hasCatalog()) {\n <input\n class=\"fb-input\"\n [attr.list]=\"'fb-updateable-' + object()\"\n [value]=\"assignment.field || ''\"\n placeholder=\"Nome del campo\"\n aria-label=\"Campo\"\n (input)=\"setField($index, $any($event.target).value)\"\n />\n <datalist id=\"fb-updateable-{{ object() }}\">\n @for (field of fieldOptions(); track field.name) {\n <option [value]=\"field.name\">{{ field.label || field.name }}</option>\n }\n </datalist>\n } @else {\n <input\n class=\"fb-input\"\n [value]=\"assignment.field || ''\"\n placeholder=\"Nome del campo\"\n aria-label=\"Campo\"\n (input)=\"setField($index, $any($event.target).value)\"\n />\n }\n <button\n type=\"button\"\n class=\"fb-btn fb-btn--ghost fb-btn--icon\"\n aria-label=\"Rimuovi\"\n (click)=\"remove($index)\"\n >\n \u00D7\n </button>\n </div>\n\n @if (isUnknown(assignment)) {\n <p class=\"fb-field__hint fb-field__hint--warn\">\n Questo campo non e\u2019 fra quelli aggiornabili dell\u2019oggetto.\n </p>\n }\n\n <fb-value-editor\n [value]=\"assignment.value\"\n [dataType]=\"describe(assignment.field)?.dataType\"\n [objectType]=\"describe(assignment.field)?.objectType || undefined\"\n label=\"Valore del campo\"\n (valueChange)=\"setValue($index, $event)\"\n />\n </div>\n } @empty {\n <p class=\"fb-empty\">Nessun campo valorizzato.</p>\n }\n </div>\n\n <button type=\"button\" class=\"fb-btn\" (click)=\"add()\">Aggiungi campo</button>\n</fieldset>\n", styles: [":host{display:block}.fb-list__header .fb-input{flex:1;min-width:0}\n"], dependencies: [{ kind: "component", type: ValueEditorComponent, selector: "fb-value-editor", inputs: ["value", "label", "dataType", "objectType", "isCollection", "disabled", "allowFormula"], outputs: ["valueChange"] }], changeDetection: i0.ChangeDetectionStrategy.OnPush });
4265
+ static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "20.3.27", type: FieldAssignmentEditorComponent, isStandalone: true, selector: "fb-field-assignment-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 }, disabledReason: { classPropertyName: "disabledReason", publicName: "disabledReason", 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 (disabledReason()) {\r\n <p class=\"fb-callout\">{{ disabledReason() }}</p>\r\n }\r\n @if (!object()) {\r\n <p class=\"fb-field__hint\">Scegli prima un oggetto per poterne valorizzare i campi.</p>\r\n }\r\n @if (missingRequired().length) {\r\n <p class=\"fb-callout fb-callout--warn\">\r\n Campi obbligatori non valorizzati:\r\n @for (field of missingRequired(); track field.name) {\r\n <button type=\"button\" class=\"fb-btn fb-btn--ghost fb-btn--icon\" (click)=\"add(field.name)\">\r\n + {{ field.label || field.name }}\r\n </button>\r\n }\r\n </p>\r\n }\r\n\r\n <div class=\"fb-list\">\r\n @for (assignment of assignments(); track $index) {\r\n <div class=\"fb-list__item\">\r\n <div class=\"fb-list__header\">\r\n @if (hasCatalog()) {\r\n <input\r\n class=\"fb-input\"\r\n [attr.list]=\"'fb-updateable-' + object()\"\r\n [value]=\"assignment.field || ''\"\r\n placeholder=\"Nome del campo\"\r\n aria-label=\"Campo\"\r\n (input)=\"setField($index, $any($event.target).value)\"\r\n />\r\n <datalist id=\"fb-updateable-{{ object() }}\">\r\n @for (field of fieldOptions(); track field.name) {\r\n <option [value]=\"field.name\">{{ field.label || field.name }}</option>\r\n }\r\n </datalist>\r\n } @else {\r\n <input\r\n class=\"fb-input\"\r\n [value]=\"assignment.field || ''\"\r\n placeholder=\"Nome del campo\"\r\n aria-label=\"Campo\"\r\n (input)=\"setField($index, $any($event.target).value)\"\r\n />\r\n }\r\n <button\r\n type=\"button\"\r\n class=\"fb-btn fb-btn--ghost fb-btn--icon\"\r\n aria-label=\"Rimuovi\"\r\n (click)=\"remove($index)\"\r\n >\r\n \u00D7\r\n </button>\r\n </div>\r\n\r\n @if (isUnknown(assignment)) {\r\n <p class=\"fb-field__hint fb-field__hint--warn\">\r\n Questo campo non e\u2019 fra quelli aggiornabili dell\u2019oggetto.\r\n </p>\r\n }\r\n\r\n <fb-value-editor\r\n [value]=\"assignment.value\"\r\n [dataType]=\"describe(assignment.field)?.dataType\"\r\n [objectType]=\"describe(assignment.field)?.objectType || undefined\"\r\n label=\"Valore del campo\"\r\n (valueChange)=\"setValue($index, $event)\"\r\n />\r\n </div>\r\n } @empty {\r\n <p class=\"fb-empty\">Nessun campo valorizzato.</p>\r\n }\r\n </div>\r\n\r\n <button type=\"button\" class=\"fb-btn\" (click)=\"add()\">Aggiungi campo</button>\r\n</fieldset>\r\n", styles: [":host{display:block}.fb-list__header .fb-input{flex:1;min-width:0}\n"], dependencies: [{ kind: "component", type: ValueEditorComponent, selector: "fb-value-editor", inputs: ["value", "label", "dataType", "objectType", "isCollection", "disabled", "allowFormula"], outputs: ["valueChange"] }], changeDetection: i0.ChangeDetectionStrategy.OnPush });
3867
4266
  }
3868
4267
  i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.27", ngImport: i0, type: FieldAssignmentEditorComponent, decorators: [{
3869
4268
  type: Component,
3870
- args: [{ selector: 'fb-field-assignment-editor', standalone: true, imports: [ValueEditorComponent], changeDetection: ChangeDetectionStrategy.OnPush, template: "<fieldset class=\"fb-section\">\n <legend class=\"fb-section__title\">{{ title() }}</legend>\n\n @if (disabledReason()) {\n <p class=\"fb-callout\">{{ disabledReason() }}</p>\n }\n @if (!object()) {\n <p class=\"fb-field__hint\">Scegli prima un oggetto per poterne valorizzare i campi.</p>\n }\n @if (missingRequired().length) {\n <p class=\"fb-callout fb-callout--warn\">\n Campi obbligatori non valorizzati:\n @for (field of missingRequired(); track field.name) {\n <button type=\"button\" class=\"fb-btn fb-btn--ghost fb-btn--icon\" (click)=\"add(field.name)\">\n + {{ field.label || field.name }}\n </button>\n }\n </p>\n }\n\n <div class=\"fb-list\">\n @for (assignment of assignments(); track $index) {\n <div class=\"fb-list__item\">\n <div class=\"fb-list__header\">\n @if (hasCatalog()) {\n <input\n class=\"fb-input\"\n [attr.list]=\"'fb-updateable-' + object()\"\n [value]=\"assignment.field || ''\"\n placeholder=\"Nome del campo\"\n aria-label=\"Campo\"\n (input)=\"setField($index, $any($event.target).value)\"\n />\n <datalist id=\"fb-updateable-{{ object() }}\">\n @for (field of fieldOptions(); track field.name) {\n <option [value]=\"field.name\">{{ field.label || field.name }}</option>\n }\n </datalist>\n } @else {\n <input\n class=\"fb-input\"\n [value]=\"assignment.field || ''\"\n placeholder=\"Nome del campo\"\n aria-label=\"Campo\"\n (input)=\"setField($index, $any($event.target).value)\"\n />\n }\n <button\n type=\"button\"\n class=\"fb-btn fb-btn--ghost fb-btn--icon\"\n aria-label=\"Rimuovi\"\n (click)=\"remove($index)\"\n >\n \u00D7\n </button>\n </div>\n\n @if (isUnknown(assignment)) {\n <p class=\"fb-field__hint fb-field__hint--warn\">\n Questo campo non e\u2019 fra quelli aggiornabili dell\u2019oggetto.\n </p>\n }\n\n <fb-value-editor\n [value]=\"assignment.value\"\n [dataType]=\"describe(assignment.field)?.dataType\"\n [objectType]=\"describe(assignment.field)?.objectType || undefined\"\n label=\"Valore del campo\"\n (valueChange)=\"setValue($index, $event)\"\n />\n </div>\n } @empty {\n <p class=\"fb-empty\">Nessun campo valorizzato.</p>\n }\n </div>\n\n <button type=\"button\" class=\"fb-btn\" (click)=\"add()\">Aggiungi campo</button>\n</fieldset>\n", styles: [":host{display:block}.fb-list__header .fb-input{flex:1;min-width:0}\n"] }]
4269
+ args: [{ selector: 'fb-field-assignment-editor', standalone: true, imports: [ValueEditorComponent], changeDetection: ChangeDetectionStrategy.OnPush, template: "<fieldset class=\"fb-section\">\r\n <legend class=\"fb-section__title\">{{ title() }}</legend>\r\n\r\n @if (disabledReason()) {\r\n <p class=\"fb-callout\">{{ disabledReason() }}</p>\r\n }\r\n @if (!object()) {\r\n <p class=\"fb-field__hint\">Scegli prima un oggetto per poterne valorizzare i campi.</p>\r\n }\r\n @if (missingRequired().length) {\r\n <p class=\"fb-callout fb-callout--warn\">\r\n Campi obbligatori non valorizzati:\r\n @for (field of missingRequired(); track field.name) {\r\n <button type=\"button\" class=\"fb-btn fb-btn--ghost fb-btn--icon\" (click)=\"add(field.name)\">\r\n + {{ field.label || field.name }}\r\n </button>\r\n }\r\n </p>\r\n }\r\n\r\n <div class=\"fb-list\">\r\n @for (assignment of assignments(); track $index) {\r\n <div class=\"fb-list__item\">\r\n <div class=\"fb-list__header\">\r\n @if (hasCatalog()) {\r\n <input\r\n class=\"fb-input\"\r\n [attr.list]=\"'fb-updateable-' + object()\"\r\n [value]=\"assignment.field || ''\"\r\n placeholder=\"Nome del campo\"\r\n aria-label=\"Campo\"\r\n (input)=\"setField($index, $any($event.target).value)\"\r\n />\r\n <datalist id=\"fb-updateable-{{ object() }}\">\r\n @for (field of fieldOptions(); track field.name) {\r\n <option [value]=\"field.name\">{{ field.label || field.name }}</option>\r\n }\r\n </datalist>\r\n } @else {\r\n <input\r\n class=\"fb-input\"\r\n [value]=\"assignment.field || ''\"\r\n placeholder=\"Nome del campo\"\r\n aria-label=\"Campo\"\r\n (input)=\"setField($index, $any($event.target).value)\"\r\n />\r\n }\r\n <button\r\n type=\"button\"\r\n class=\"fb-btn fb-btn--ghost fb-btn--icon\"\r\n aria-label=\"Rimuovi\"\r\n (click)=\"remove($index)\"\r\n >\r\n \u00D7\r\n </button>\r\n </div>\r\n\r\n @if (isUnknown(assignment)) {\r\n <p class=\"fb-field__hint fb-field__hint--warn\">\r\n Questo campo non e\u2019 fra quelli aggiornabili dell\u2019oggetto.\r\n </p>\r\n }\r\n\r\n <fb-value-editor\r\n [value]=\"assignment.value\"\r\n [dataType]=\"describe(assignment.field)?.dataType\"\r\n [objectType]=\"describe(assignment.field)?.objectType || undefined\"\r\n label=\"Valore del campo\"\r\n (valueChange)=\"setValue($index, $event)\"\r\n />\r\n </div>\r\n } @empty {\r\n <p class=\"fb-empty\">Nessun campo valorizzato.</p>\r\n }\r\n </div>\r\n\r\n <button type=\"button\" class=\"fb-btn\" (click)=\"add()\">Aggiungi campo</button>\r\n</fieldset>\r\n", styles: [":host{display:block}.fb-list__header .fb-input{flex:1;min-width:0}\n"] }]
3871
4270
  }], 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 }] }], disabledReason: [{ type: i0.Input, args: [{ isSignal: true, alias: "disabledReason", required: false }] }], changed: [{ type: i0.Output, args: ["changed"] }] } });
3872
4271
 
3873
4272
  /**
@@ -4170,8 +4569,9 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.27", ngImpo
4170
4569
  * 1. **Le operazioni sono eseguite nell'ordine dichiarato**, quindi devono essere
4171
4570
  * riordinabili: un'interfaccia che le presenta come un insieme nasconde la semantica.
4172
4571
  * 2. **Cosa puo' stare in `assignToReference`**: una variabile, un campo di una variabile
4173
- * record, un campo di `$Record` e — sole eccezioni fra le globali
4174
- * `$Flow.CurrentStage` e `$Flow.ActiveStages`. Tutto il resto e' di sola lettura.
4572
+ * record, un campo di `$Record` e, fra le globali, `$Flow.CurrentStage`,
4573
+ * `$Flow.ActiveStages` e quelle che il sistema ospite dichiara scrivibili — le propone
4574
+ * già `POST /flows/references/writable`. Tutto il resto e' di sola lettura.
4175
4575
  * 3. Gli operatori marcati `expectsCollection` hanno senso **solo** su destinazioni
4176
4576
  * collection: su una variabile singola non falliscono, non fanno nulla.
4177
4577
  */
@@ -4180,8 +4580,6 @@ class AssignmentInspectorComponent extends NodeInspectorBase {
4180
4580
  assignment = computed(() => this.node(), ...(ngDevMode ? [{ debugName: "assignment" }] : []));
4181
4581
  items = computed(() => this.assignment().assignmentItems ?? [], ...(ngDevMode ? [{ debugName: "items" }] : []));
4182
4582
  operators = computed(() => this.dictionaries.assignmentOperators(), ...(ngDevMode ? [{ debugName: "operators" }] : []));
4183
- /** Il flow dichiara stage: solo allora le due globali di stage sono proponibili (§5.2). */
4184
- hasStages = computed(() => (this.store.document().stages?.length ?? 0) > 0, ...(ngDevMode ? [{ debugName: "hasStages" }] : []));
4185
4583
  operatorLabel(value) {
4186
4584
  if (!value) {
4187
4585
  return '';
@@ -4265,11 +4663,11 @@ class AssignmentInspectorComponent extends NodeInspectorBase {
4265
4663
  return !operator.startsWith('Remove') || operator === 'RemovePosition' || operator === 'RemoveUncommon';
4266
4664
  }
4267
4665
  static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.3.27", ngImport: i0, type: AssignmentInspectorComponent, deps: null, target: i0.ɵɵFactoryTarget.Component });
4268
- static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "20.3.27", type: AssignmentInspectorComponent, isStandalone: true, selector: "fb-assignment-inspector", usesInheritance: true, ngImport: i0, template: "<fieldset class=\"fb-section\">\r\n <legend class=\"fb-section__title\">Operazioni</legend>\r\n <p class=\"fb-section__note\">\r\n Le operazioni sono eseguite <strong>nell\u2019ordine in cui compaiono</strong>: spostarne una cambia il\r\n risultato.\r\n </p>\r\n\r\n <div class=\"fb-list\">\r\n @for (item of items(); 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=\"Sposta su\"\r\n [disabled]=\"$first\"\r\n (click)=\"moveItem($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)=\"moveItem($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 l\u2019operazione\"\r\n (click)=\"removeItem($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 fb-field__label--required\">Destinazione</label>\r\n <!--\r\n Solo destinazioni scrivibili: una costante o una formula produrrebbero\r\n TARGET_NOT_WRITABLE. Le due globali di stage si aggiungono solo se il flow\r\n dichiara stage.\r\n -->\r\n <fb-reference-picker\r\n [value]=\"item.assignToReference\"\r\n [writableOnly]=\"true\"\r\n [allowStageTargets]=\"hasStages()\"\r\n placeholder=\"Scegli una variabile\"\r\n (valueChange)=\"setTarget($index, $event)\"\r\n />\r\n <p class=\"fb-field__hint\">\r\n Puoi scrivere anche un campo di un record: <code>Cliente.Email</code>, <code>$Record.Stato</code>.\r\n </p>\r\n </div>\r\n\r\n <div class=\"fb-field\">\r\n <label class=\"fb-field__label\">Operazione</label>\r\n <select\r\n class=\"fb-select\"\r\n [fbValue]=\"item.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 @if (operatorDescription(item.operator)) {\r\n <p class=\"fb-field__hint\">{{ operatorDescription(item.operator) }}</p>\r\n }\r\n @if (addSemanticsHint(item)) {\r\n <p class=\"fb-field__hint\">{{ addSemanticsHint(item) }}</p>\r\n }\r\n @if (expectsCollection(item)) {\r\n <p class=\"fb-field__hint fb-field__hint--warn\">\r\n Questa operazione ha senso solo su una destinazione collection: su una variabile singola non\r\n fallisce, semplicemente non fa nulla.\r\n </p>\r\n }\r\n </div>\r\n\r\n @if (needsValue(item)) {\r\n <div class=\"fb-field\">\r\n <label class=\"fb-field__label\">Valore</label>\r\n <fb-value-editor [value]=\"item.value\" label=\"Valore\" (valueChange)=\"setValue($index, $event)\" />\r\n </div>\r\n }\r\n </div>\r\n } @empty {\r\n <p class=\"fb-empty\">\r\n Nessuna operazione: un Assignment senza operazioni non fa nulla (ASSIGNMENT_WITHOUT_ITEMS).\r\n </p>\r\n }\r\n </div>\r\n\r\n <button type=\"button\" class=\"fb-btn\" (click)=\"addItem()\">Aggiungi operazione</button>\r\n</fieldset>\r\n\r\n<fb-connector-editor\r\n [nodeName]=\"name()\"\r\n [node]=\"node()\"\r\n [outlets]=\"outlets()\"\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: ReferencePickerComponent, selector: "fb-reference-picker", inputs: ["value", "label", "placeholder", "disabled", "dataType", "isCollection", "objectType", "writableOnly", "allowStageTargets", "elementsOnly"], outputs: ["valueChange"] }, { kind: "component", type: ValueEditorComponent, selector: "fb-value-editor", inputs: ["value", "label", "dataType", "objectType", "isCollection", "disabled", "allowFormula"], outputs: ["valueChange"] }, { kind: "directive", type: SelectValueDirective, selector: "select[fbValue]", inputs: ["fbValue"] }], changeDetection: i0.ChangeDetectionStrategy.OnPush });
4666
+ static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "20.3.27", type: AssignmentInspectorComponent, isStandalone: true, selector: "fb-assignment-inspector", usesInheritance: true, ngImport: i0, template: "<fieldset class=\"fb-section\">\r\n <legend class=\"fb-section__title\">Operazioni</legend>\r\n <p class=\"fb-section__note\">\r\n Le operazioni sono eseguite <strong>nell\u2019ordine in cui compaiono</strong>: spostarne una cambia il\r\n risultato.\r\n </p>\r\n\r\n <div class=\"fb-list\">\r\n @for (item of items(); 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=\"Sposta su\"\r\n [disabled]=\"$first\"\r\n (click)=\"moveItem($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)=\"moveItem($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 l\u2019operazione\"\r\n (click)=\"removeItem($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 fb-field__label--required\">Destinazione</label>\r\n <!--\r\n Solo destinazioni scrivibili: una costante o una formula produrrebbero\r\n TARGET_NOT_WRITABLE. Le globali assegnabili \u2014 `$Flow.CurrentStage`,\r\n `$Flow.ActiveStages` e quelle dichiarate scrivibili dall'host \u2014 arrivano gi\u00E0\r\n dalla primitiva, non si aggiungono qui.\r\n -->\r\n <fb-reference-picker\r\n [value]=\"item.assignToReference\"\r\n [writableOnly]=\"true\"\r\n placeholder=\"Scegli una variabile\"\r\n (valueChange)=\"setTarget($index, $event)\"\r\n />\r\n <p class=\"fb-field__hint\">\r\n Puoi scrivere anche un campo di un record: <code>Cliente.Email</code>, <code>$Record.Stato</code>.\r\n </p>\r\n </div>\r\n\r\n <div class=\"fb-field\">\r\n <label class=\"fb-field__label\">Operazione</label>\r\n <select\r\n class=\"fb-select\"\r\n [fbValue]=\"item.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 @if (operatorDescription(item.operator)) {\r\n <p class=\"fb-field__hint\">{{ operatorDescription(item.operator) }}</p>\r\n }\r\n @if (addSemanticsHint(item)) {\r\n <p class=\"fb-field__hint\">{{ addSemanticsHint(item) }}</p>\r\n }\r\n @if (expectsCollection(item)) {\r\n <p class=\"fb-field__hint fb-field__hint--warn\">\r\n Questa operazione ha senso solo su una destinazione collection: su una variabile singola non\r\n fallisce, semplicemente non fa nulla.\r\n </p>\r\n }\r\n </div>\r\n\r\n @if (needsValue(item)) {\r\n <div class=\"fb-field\">\r\n <label class=\"fb-field__label\">Valore</label>\r\n <fb-value-editor [value]=\"item.value\" label=\"Valore\" (valueChange)=\"setValue($index, $event)\" />\r\n </div>\r\n }\r\n </div>\r\n } @empty {\r\n <p class=\"fb-empty\">\r\n Nessuna operazione: un Assignment senza operazioni non fa nulla (ASSIGNMENT_WITHOUT_ITEMS).\r\n </p>\r\n }\r\n </div>\r\n\r\n <button type=\"button\" class=\"fb-btn\" (click)=\"addItem()\">Aggiungi operazione</button>\r\n</fieldset>\r\n\r\n<fb-connector-editor\r\n [nodeName]=\"name()\"\r\n [node]=\"node()\"\r\n [outlets]=\"outlets()\"\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: ReferencePickerComponent, selector: "fb-reference-picker", inputs: ["value", "label", "placeholder", "disabled", "dataType", "isCollection", "objectType", "writableOnly", "elementsOnly"], outputs: ["valueChange"] }, { kind: "component", type: ValueEditorComponent, selector: "fb-value-editor", inputs: ["value", "label", "dataType", "objectType", "isCollection", "disabled", "allowFormula"], outputs: ["valueChange"] }, { kind: "directive", type: SelectValueDirective, selector: "select[fbValue]", inputs: ["fbValue"] }], changeDetection: i0.ChangeDetectionStrategy.OnPush });
4269
4667
  }
4270
4668
  i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.27", ngImport: i0, type: AssignmentInspectorComponent, decorators: [{
4271
4669
  type: Component,
4272
- args: [{ selector: 'fb-assignment-inspector', standalone: true, imports: [ConnectorEditorComponent, ReferencePickerComponent, ValueEditorComponent, SelectValueDirective], changeDetection: ChangeDetectionStrategy.OnPush, template: "<fieldset class=\"fb-section\">\r\n <legend class=\"fb-section__title\">Operazioni</legend>\r\n <p class=\"fb-section__note\">\r\n Le operazioni sono eseguite <strong>nell\u2019ordine in cui compaiono</strong>: spostarne una cambia il\r\n risultato.\r\n </p>\r\n\r\n <div class=\"fb-list\">\r\n @for (item of items(); 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=\"Sposta su\"\r\n [disabled]=\"$first\"\r\n (click)=\"moveItem($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)=\"moveItem($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 l\u2019operazione\"\r\n (click)=\"removeItem($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 fb-field__label--required\">Destinazione</label>\r\n <!--\r\n Solo destinazioni scrivibili: una costante o una formula produrrebbero\r\n TARGET_NOT_WRITABLE. Le due globali di stage si aggiungono solo se il flow\r\n dichiara stage.\r\n -->\r\n <fb-reference-picker\r\n [value]=\"item.assignToReference\"\r\n [writableOnly]=\"true\"\r\n [allowStageTargets]=\"hasStages()\"\r\n placeholder=\"Scegli una variabile\"\r\n (valueChange)=\"setTarget($index, $event)\"\r\n />\r\n <p class=\"fb-field__hint\">\r\n Puoi scrivere anche un campo di un record: <code>Cliente.Email</code>, <code>$Record.Stato</code>.\r\n </p>\r\n </div>\r\n\r\n <div class=\"fb-field\">\r\n <label class=\"fb-field__label\">Operazione</label>\r\n <select\r\n class=\"fb-select\"\r\n [fbValue]=\"item.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 @if (operatorDescription(item.operator)) {\r\n <p class=\"fb-field__hint\">{{ operatorDescription(item.operator) }}</p>\r\n }\r\n @if (addSemanticsHint(item)) {\r\n <p class=\"fb-field__hint\">{{ addSemanticsHint(item) }}</p>\r\n }\r\n @if (expectsCollection(item)) {\r\n <p class=\"fb-field__hint fb-field__hint--warn\">\r\n Questa operazione ha senso solo su una destinazione collection: su una variabile singola non\r\n fallisce, semplicemente non fa nulla.\r\n </p>\r\n }\r\n </div>\r\n\r\n @if (needsValue(item)) {\r\n <div class=\"fb-field\">\r\n <label class=\"fb-field__label\">Valore</label>\r\n <fb-value-editor [value]=\"item.value\" label=\"Valore\" (valueChange)=\"setValue($index, $event)\" />\r\n </div>\r\n }\r\n </div>\r\n } @empty {\r\n <p class=\"fb-empty\">\r\n Nessuna operazione: un Assignment senza operazioni non fa nulla (ASSIGNMENT_WITHOUT_ITEMS).\r\n </p>\r\n }\r\n </div>\r\n\r\n <button type=\"button\" class=\"fb-btn\" (click)=\"addItem()\">Aggiungi operazione</button>\r\n</fieldset>\r\n\r\n<fb-connector-editor\r\n [nodeName]=\"name()\"\r\n [node]=\"node()\"\r\n [outlets]=\"outlets()\"\r\n (connectorChanged)=\"onConnectorChanged($event)\"\r\n/>\r\n" }]
4670
+ args: [{ selector: 'fb-assignment-inspector', standalone: true, imports: [ConnectorEditorComponent, ReferencePickerComponent, ValueEditorComponent, SelectValueDirective], changeDetection: ChangeDetectionStrategy.OnPush, template: "<fieldset class=\"fb-section\">\r\n <legend class=\"fb-section__title\">Operazioni</legend>\r\n <p class=\"fb-section__note\">\r\n Le operazioni sono eseguite <strong>nell\u2019ordine in cui compaiono</strong>: spostarne una cambia il\r\n risultato.\r\n </p>\r\n\r\n <div class=\"fb-list\">\r\n @for (item of items(); 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=\"Sposta su\"\r\n [disabled]=\"$first\"\r\n (click)=\"moveItem($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)=\"moveItem($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 l\u2019operazione\"\r\n (click)=\"removeItem($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 fb-field__label--required\">Destinazione</label>\r\n <!--\r\n Solo destinazioni scrivibili: una costante o una formula produrrebbero\r\n TARGET_NOT_WRITABLE. Le globali assegnabili \u2014 `$Flow.CurrentStage`,\r\n `$Flow.ActiveStages` e quelle dichiarate scrivibili dall'host \u2014 arrivano gi\u00E0\r\n dalla primitiva, non si aggiungono qui.\r\n -->\r\n <fb-reference-picker\r\n [value]=\"item.assignToReference\"\r\n [writableOnly]=\"true\"\r\n placeholder=\"Scegli una variabile\"\r\n (valueChange)=\"setTarget($index, $event)\"\r\n />\r\n <p class=\"fb-field__hint\">\r\n Puoi scrivere anche un campo di un record: <code>Cliente.Email</code>, <code>$Record.Stato</code>.\r\n </p>\r\n </div>\r\n\r\n <div class=\"fb-field\">\r\n <label class=\"fb-field__label\">Operazione</label>\r\n <select\r\n class=\"fb-select\"\r\n [fbValue]=\"item.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 @if (operatorDescription(item.operator)) {\r\n <p class=\"fb-field__hint\">{{ operatorDescription(item.operator) }}</p>\r\n }\r\n @if (addSemanticsHint(item)) {\r\n <p class=\"fb-field__hint\">{{ addSemanticsHint(item) }}</p>\r\n }\r\n @if (expectsCollection(item)) {\r\n <p class=\"fb-field__hint fb-field__hint--warn\">\r\n Questa operazione ha senso solo su una destinazione collection: su una variabile singola non\r\n fallisce, semplicemente non fa nulla.\r\n </p>\r\n }\r\n </div>\r\n\r\n @if (needsValue(item)) {\r\n <div class=\"fb-field\">\r\n <label class=\"fb-field__label\">Valore</label>\r\n <fb-value-editor [value]=\"item.value\" label=\"Valore\" (valueChange)=\"setValue($index, $event)\" />\r\n </div>\r\n }\r\n </div>\r\n } @empty {\r\n <p class=\"fb-empty\">\r\n Nessuna operazione: un Assignment senza operazioni non fa nulla (ASSIGNMENT_WITHOUT_ITEMS).\r\n </p>\r\n }\r\n </div>\r\n\r\n <button type=\"button\" class=\"fb-btn\" (click)=\"addItem()\">Aggiungi operazione</button>\r\n</fieldset>\r\n\r\n<fb-connector-editor\r\n [nodeName]=\"name()\"\r\n [node]=\"node()\"\r\n [outlets]=\"outlets()\"\r\n (connectorChanged)=\"onConnectorChanged($event)\"\r\n/>\r\n" }]
4273
4671
  }] });
4274
4672
 
4275
4673
  /**
@@ -4386,7 +4784,7 @@ class CollectionProcessorInspectorComponent extends NodeInspectorBase {
4386
4784
  this.patch((node) => mutate(node));
4387
4785
  }
4388
4786
  static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.3.27", ngImport: i0, type: CollectionProcessorInspectorComponent, deps: null, target: i0.ɵɵFactoryTarget.Component });
4389
- static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "20.3.27", type: CollectionProcessorInspectorComponent, isStandalone: true, selector: "fb-collection-processor-inspector", usesInheritance: true, ngImport: i0, template: "<div class=\"fb-field\">\r\n <label class=\"fb-field__label fb-field__label--required\">Cosa fa</label>\r\n <select\r\n class=\"fb-select\"\r\n [fbValue]=\"processor().collectionProcessorType || ''\"\r\n (change)=\"setKind($any($event.target).value)\"\r\n >\r\n <option value=\"\">\u2014 scegli \u2014</option>\r\n @for (kind of kinds(); track kind.value) {\r\n <option [value]=\"kind.value\">{{ kind.label }}</option>\r\n }\r\n </select>\r\n @if (!hasKind()) {\r\n <p class=\"fb-field__error\">Senza questo valore l\u2019elemento non e\u2019 eseguibile (PROCESSOR_TYPE_MISSING).</p>\r\n }\r\n</div>\r\n\r\n<div class=\"fb-field\">\r\n <label class=\"fb-field__label fb-field__label--required\">Collection di partenza</label>\r\n <fb-reference-picker\r\n [value]=\"processor().collectionReference\"\r\n [isCollection]=\"true\"\r\n placeholder=\"Scegli una collection\"\r\n (valueChange)=\"setCollection($event)\"\r\n />\r\n</div>\r\n\r\n<p class=\"fb-callout\">\r\n Il risultato non si assegna a una variabile: e\u2019 l\u2019<strong>output automatico</strong> dell\u2019elemento, e si\r\n referenzia col suo nome \u2014 <code>{{ name() }}</code>.\r\n</p>\r\n\r\n@if (isSort()) {\r\n <fieldset class=\"fb-section\">\r\n <legend class=\"fb-section__title\">Ordinamento</legend>\r\n <div class=\"fb-list\">\r\n @for (option of sortOptions(); 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=\"Sposta su\"\r\n [disabled]=\"$first\"\r\n (click)=\"moveSortOption($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)=\"moveSortOption($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\"\r\n (click)=\"removeSortOption($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\">Campo</label>\r\n <input\r\n class=\"fb-input\"\r\n [value]=\"option.sortField || ''\"\r\n placeholder=\"Nome del campo degli elementi\"\r\n (input)=\"setSortField($index, $any($event.target).value)\"\r\n />\r\n </div>\r\n\r\n <div class=\"fb-field\">\r\n <label class=\"fb-field__label\">Direzione</label>\r\n <select\r\n class=\"fb-select\"\r\n [fbValue]=\"option.sortOrder || ''\"\r\n (change)=\"setSortOrder($index, $any($event.target).value)\"\r\n >\r\n @for (order of sortOrders(); track order.value) {\r\n <option [value]=\"order.value\">{{ order.label }}</option>\r\n }\r\n </select>\r\n </div>\r\n\r\n <label class=\"fb-check\">\r\n <input\r\n type=\"checkbox\"\r\n [checked]=\"option.doesPutEmptyStringAndNullFirst === true\"\r\n (change)=\"setEmptyFirst($index, $any($event.target).checked)\"\r\n />\r\n Metti prima i valori vuoti e nulli\r\n </label>\r\n </div>\r\n } @empty {\r\n <p class=\"fb-empty\">Un Sort senza criteri di ordinamento e\u2019 un errore (SORT_OPTIONS_MISSING).</p>\r\n }\r\n </div>\r\n <button type=\"button\" class=\"fb-btn\" (click)=\"addSortOption()\">Aggiungi criterio</button>\r\n </fieldset>\r\n\r\n <div class=\"fb-field\">\r\n <label class=\"fb-field__label\">Numero massimo di elementi</label>\r\n <input\r\n class=\"fb-input\"\r\n type=\"number\"\r\n min=\"1\"\r\n [value]=\"processor().limit ?? ''\"\r\n (input)=\"setLimit($any($event.target).value)\"\r\n />\r\n </div>\r\n}\r\n\r\n@if (isFilter()) {\r\n <div class=\"fb-field\">\r\n <!--\r\n Trappola: questo NON e' dove finisce il risultato. \u00C8 la variabile che espone alle\r\n condizioni l'elemento che si sta esaminando, esattamente come nel Loop.\r\n -->\r\n <label class=\"fb-field__label fb-field__label--required\">Elemento in esame</label>\r\n <fb-reference-picker\r\n [value]=\"processor().assignNextValueToReference\"\r\n [writableOnly]=\"true\"\r\n [dataType]=\"elementDataType()\"\r\n [objectType]=\"elementObjectType()\"\r\n [isCollection]=\"false\"\r\n placeholder=\"Variabile che espone l\u2019elemento alle condizioni\"\r\n (valueChange)=\"setCurrentElementVariable($event)\"\r\n />\r\n <p class=\"fb-field__hint\">\r\n Questa variabile serve <strong>a scrivere le condizioni</strong> qui sotto: non e\u2019 la destinazione del\r\n risultato.\r\n </p>\r\n </div>\r\n\r\n <fb-condition-editor\r\n [holder]=\"processor()\"\r\n title=\"Tieni gli elementi per cui\"\r\n (changed)=\"onConditionsChanged($event)\"\r\n />\r\n}\r\n\r\n<fb-connector-editor\r\n [nodeName]=\"name()\"\r\n [node]=\"node()\"\r\n [outlets]=\"outlets()\"\r\n (connectorChanged)=\"onConnectorChanged($event)\"\r\n/>\r\n", dependencies: [{ kind: "component", type: ConditionEditorComponent, selector: "fb-condition-editor", inputs: ["holder", "title", "allowFormula", "issuePath"], outputs: ["changed"] }, { kind: "component", type: ConnectorEditorComponent, selector: "fb-connector-editor", inputs: ["nodeName", "node", "outlets", "title"], outputs: ["connectorChanged"] }, { kind: "component", type: ReferencePickerComponent, selector: "fb-reference-picker", inputs: ["value", "label", "placeholder", "disabled", "dataType", "isCollection", "objectType", "writableOnly", "allowStageTargets", "elementsOnly"], outputs: ["valueChange"] }, { kind: "directive", type: SelectValueDirective, selector: "select[fbValue]", inputs: ["fbValue"] }], changeDetection: i0.ChangeDetectionStrategy.OnPush });
4787
+ static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "20.3.27", type: CollectionProcessorInspectorComponent, isStandalone: true, selector: "fb-collection-processor-inspector", usesInheritance: true, ngImport: i0, template: "<div class=\"fb-field\">\r\n <label class=\"fb-field__label fb-field__label--required\">Cosa fa</label>\r\n <select\r\n class=\"fb-select\"\r\n [fbValue]=\"processor().collectionProcessorType || ''\"\r\n (change)=\"setKind($any($event.target).value)\"\r\n >\r\n <option value=\"\">\u2014 scegli \u2014</option>\r\n @for (kind of kinds(); track kind.value) {\r\n <option [value]=\"kind.value\">{{ kind.label }}</option>\r\n }\r\n </select>\r\n @if (!hasKind()) {\r\n <p class=\"fb-field__error\">Senza questo valore l\u2019elemento non e\u2019 eseguibile (PROCESSOR_TYPE_MISSING).</p>\r\n }\r\n</div>\r\n\r\n<div class=\"fb-field\">\r\n <label class=\"fb-field__label fb-field__label--required\">Collection di partenza</label>\r\n <fb-reference-picker\r\n [value]=\"processor().collectionReference\"\r\n [isCollection]=\"true\"\r\n placeholder=\"Scegli una collection\"\r\n (valueChange)=\"setCollection($event)\"\r\n />\r\n</div>\r\n\r\n<p class=\"fb-callout\">\r\n Il risultato non si assegna a una variabile: e\u2019 l\u2019<strong>output automatico</strong> dell\u2019elemento, e si\r\n referenzia col suo nome \u2014 <code>{{ name() }}</code>.\r\n</p>\r\n\r\n@if (isSort()) {\r\n <fieldset class=\"fb-section\">\r\n <legend class=\"fb-section__title\">Ordinamento</legend>\r\n <div class=\"fb-list\">\r\n @for (option of sortOptions(); 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=\"Sposta su\"\r\n [disabled]=\"$first\"\r\n (click)=\"moveSortOption($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)=\"moveSortOption($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\"\r\n (click)=\"removeSortOption($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\">Campo</label>\r\n <input\r\n class=\"fb-input\"\r\n [value]=\"option.sortField || ''\"\r\n placeholder=\"Nome del campo degli elementi\"\r\n (input)=\"setSortField($index, $any($event.target).value)\"\r\n />\r\n </div>\r\n\r\n <div class=\"fb-field\">\r\n <label class=\"fb-field__label\">Direzione</label>\r\n <select\r\n class=\"fb-select\"\r\n [fbValue]=\"option.sortOrder || ''\"\r\n (change)=\"setSortOrder($index, $any($event.target).value)\"\r\n >\r\n @for (order of sortOrders(); track order.value) {\r\n <option [value]=\"order.value\">{{ order.label }}</option>\r\n }\r\n </select>\r\n </div>\r\n\r\n <label class=\"fb-check\">\r\n <input\r\n type=\"checkbox\"\r\n [checked]=\"option.doesPutEmptyStringAndNullFirst === true\"\r\n (change)=\"setEmptyFirst($index, $any($event.target).checked)\"\r\n />\r\n Metti prima i valori vuoti e nulli\r\n </label>\r\n </div>\r\n } @empty {\r\n <p class=\"fb-empty\">Un Sort senza criteri di ordinamento e\u2019 un errore (SORT_OPTIONS_MISSING).</p>\r\n }\r\n </div>\r\n <button type=\"button\" class=\"fb-btn\" (click)=\"addSortOption()\">Aggiungi criterio</button>\r\n </fieldset>\r\n\r\n <div class=\"fb-field\">\r\n <label class=\"fb-field__label\">Numero massimo di elementi</label>\r\n <input\r\n class=\"fb-input\"\r\n type=\"number\"\r\n min=\"1\"\r\n [value]=\"processor().limit ?? ''\"\r\n (input)=\"setLimit($any($event.target).value)\"\r\n />\r\n </div>\r\n}\r\n\r\n@if (isFilter()) {\r\n <div class=\"fb-field\">\r\n <!--\r\n Trappola: questo NON e' dove finisce il risultato. \u00C8 la variabile che espone alle\r\n condizioni l'elemento che si sta esaminando, esattamente come nel Loop.\r\n -->\r\n <label class=\"fb-field__label fb-field__label--required\">Elemento in esame</label>\r\n <fb-reference-picker\r\n [value]=\"processor().assignNextValueToReference\"\r\n [writableOnly]=\"true\"\r\n [dataType]=\"elementDataType()\"\r\n [objectType]=\"elementObjectType()\"\r\n [isCollection]=\"false\"\r\n placeholder=\"Variabile che espone l\u2019elemento alle condizioni\"\r\n (valueChange)=\"setCurrentElementVariable($event)\"\r\n />\r\n <p class=\"fb-field__hint\">\r\n Questa variabile serve <strong>a scrivere le condizioni</strong> qui sotto: non e\u2019 la destinazione del\r\n risultato.\r\n </p>\r\n </div>\r\n\r\n <fb-condition-editor\r\n [holder]=\"processor()\"\r\n title=\"Tieni gli elementi per cui\"\r\n (changed)=\"onConditionsChanged($event)\"\r\n />\r\n}\r\n\r\n<fb-connector-editor\r\n [nodeName]=\"name()\"\r\n [node]=\"node()\"\r\n [outlets]=\"outlets()\"\r\n (connectorChanged)=\"onConnectorChanged($event)\"\r\n/>\r\n", dependencies: [{ kind: "component", type: ConditionEditorComponent, selector: "fb-condition-editor", inputs: ["holder", "title", "allowFormula", "allowLogic", "issuePath"], outputs: ["changed"] }, { kind: "component", type: ConnectorEditorComponent, selector: "fb-connector-editor", inputs: ["nodeName", "node", "outlets", "title"], outputs: ["connectorChanged"] }, { kind: "component", type: ReferencePickerComponent, selector: "fb-reference-picker", inputs: ["value", "label", "placeholder", "disabled", "dataType", "isCollection", "objectType", "writableOnly", "elementsOnly"], outputs: ["valueChange"] }, { kind: "directive", type: SelectValueDirective, selector: "select[fbValue]", inputs: ["fbValue"] }], changeDetection: i0.ChangeDetectionStrategy.OnPush });
4390
4788
  }
4391
4789
  i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.27", ngImport: i0, type: CollectionProcessorInspectorComponent, decorators: [{
4392
4790
  type: Component,
@@ -4453,11 +4851,11 @@ class CustomErrorInspectorComponent extends NodeInspectorBase {
4453
4851
  });
4454
4852
  }
4455
4853
  static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.3.27", ngImport: i0, type: CustomErrorInspectorComponent, deps: null, target: i0.ɵɵFactoryTarget.Component });
4456
- static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "20.3.27", type: CustomErrorInspectorComponent, isStandalone: true, selector: "fb-custom-error-inspector", usesInheritance: true, ngImport: i0, template: "<p class=\"fb-callout\">\n Questo elemento <strong>interrompe</strong> l\u2019esecuzione e restituisce i messaggi al chiamante. Non ha\n uscite: quello che c\u2019e\u2019 dopo non verra\u2019 eseguito.\n</p>\n\n<div class=\"fb-list\">\n @for (message of messages(); track $index) {\n <div class=\"fb-list__item\">\n <div class=\"fb-list__header\">\n <span class=\"fb-list__index\">{{ $index + 1 }}</span>\n <span class=\"fb-list__spacer\"></span>\n <button\n type=\"button\"\n class=\"fb-btn fb-btn--ghost fb-btn--icon\"\n aria-label=\"Rimuovi il messaggio\"\n (click)=\"removeMessage($index)\"\n >\n \u00D7\n </button>\n </div>\n\n <div class=\"fb-field\">\n <label class=\"fb-field__label fb-field__label--required\">Messaggio</label>\n <textarea\n class=\"fb-textarea\"\n [value]=\"message.errorMessage || ''\"\n placeholder=\"L\u2019importo supera il limite consentito.\"\n (input)=\"setMessage($index, $any($event.target).value)\"\n ></textarea>\n </div>\n\n <label class=\"fb-check\">\n <input\n type=\"checkbox\"\n [checked]=\"message.isFieldError === true\"\n (change)=\"setIsFieldError($index, $any($event.target).checked)\"\n />\n Mostra accanto a un campo\n </label>\n\n @if (message.isFieldError) {\n <div class=\"fb-field\">\n <label class=\"fb-field__label fb-field__label--required\">Campo</label>\n <input\n class=\"fb-input\"\n [value]=\"message.fieldSelection || ''\"\n placeholder=\"Importo\"\n (input)=\"setFieldSelection($index, $any($event.target).value)\"\n />\n </div>\n }\n </div>\n } @empty {\n <p class=\"fb-empty\">\n Almeno un messaggio e\u2019 obbligatorio: sono il prodotto di questo elemento\n (CUSTOM_ERROR_WITHOUT_MESSAGES).\n </p>\n }\n</div>\n\n<button type=\"button\" class=\"fb-btn\" (click)=\"addMessage()\">Aggiungi messaggio</button>\n", changeDetection: i0.ChangeDetectionStrategy.OnPush });
4854
+ static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "20.3.27", type: CustomErrorInspectorComponent, isStandalone: true, selector: "fb-custom-error-inspector", usesInheritance: true, ngImport: i0, template: "<p class=\"fb-callout\">\r\n Questo elemento <strong>interrompe</strong> l\u2019esecuzione e restituisce i messaggi al chiamante. Non ha\r\n uscite: quello che c\u2019e\u2019 dopo non verra\u2019 eseguito.\r\n</p>\r\n\r\n<div class=\"fb-list\">\r\n @for (message of messages(); 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 messaggio\"\r\n (click)=\"removeMessage($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 fb-field__label--required\">Messaggio</label>\r\n <textarea\r\n class=\"fb-textarea\"\r\n [value]=\"message.errorMessage || ''\"\r\n placeholder=\"L\u2019importo supera il limite consentito.\"\r\n (input)=\"setMessage($index, $any($event.target).value)\"\r\n ></textarea>\r\n </div>\r\n\r\n <label class=\"fb-check\">\r\n <input\r\n type=\"checkbox\"\r\n [checked]=\"message.isFieldError === true\"\r\n (change)=\"setIsFieldError($index, $any($event.target).checked)\"\r\n />\r\n Mostra accanto a un campo\r\n </label>\r\n\r\n @if (message.isFieldError) {\r\n <div class=\"fb-field\">\r\n <label class=\"fb-field__label fb-field__label--required\">Campo</label>\r\n <input\r\n class=\"fb-input\"\r\n [value]=\"message.fieldSelection || ''\"\r\n placeholder=\"Importo\"\r\n (input)=\"setFieldSelection($index, $any($event.target).value)\"\r\n />\r\n </div>\r\n }\r\n </div>\r\n } @empty {\r\n <p class=\"fb-empty\">\r\n Almeno un messaggio e\u2019 obbligatorio: sono il prodotto di questo elemento\r\n (CUSTOM_ERROR_WITHOUT_MESSAGES).\r\n </p>\r\n }\r\n</div>\r\n\r\n<button type=\"button\" class=\"fb-btn\" (click)=\"addMessage()\">Aggiungi messaggio</button>\r\n", changeDetection: i0.ChangeDetectionStrategy.OnPush });
4457
4855
  }
4458
4856
  i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.27", ngImport: i0, type: CustomErrorInspectorComponent, decorators: [{
4459
4857
  type: Component,
4460
- args: [{ selector: 'fb-custom-error-inspector', standalone: true, changeDetection: ChangeDetectionStrategy.OnPush, template: "<p class=\"fb-callout\">\n Questo elemento <strong>interrompe</strong> l\u2019esecuzione e restituisce i messaggi al chiamante. Non ha\n uscite: quello che c\u2019e\u2019 dopo non verra\u2019 eseguito.\n</p>\n\n<div class=\"fb-list\">\n @for (message of messages(); track $index) {\n <div class=\"fb-list__item\">\n <div class=\"fb-list__header\">\n <span class=\"fb-list__index\">{{ $index + 1 }}</span>\n <span class=\"fb-list__spacer\"></span>\n <button\n type=\"button\"\n class=\"fb-btn fb-btn--ghost fb-btn--icon\"\n aria-label=\"Rimuovi il messaggio\"\n (click)=\"removeMessage($index)\"\n >\n \u00D7\n </button>\n </div>\n\n <div class=\"fb-field\">\n <label class=\"fb-field__label fb-field__label--required\">Messaggio</label>\n <textarea\n class=\"fb-textarea\"\n [value]=\"message.errorMessage || ''\"\n placeholder=\"L\u2019importo supera il limite consentito.\"\n (input)=\"setMessage($index, $any($event.target).value)\"\n ></textarea>\n </div>\n\n <label class=\"fb-check\">\n <input\n type=\"checkbox\"\n [checked]=\"message.isFieldError === true\"\n (change)=\"setIsFieldError($index, $any($event.target).checked)\"\n />\n Mostra accanto a un campo\n </label>\n\n @if (message.isFieldError) {\n <div class=\"fb-field\">\n <label class=\"fb-field__label fb-field__label--required\">Campo</label>\n <input\n class=\"fb-input\"\n [value]=\"message.fieldSelection || ''\"\n placeholder=\"Importo\"\n (input)=\"setFieldSelection($index, $any($event.target).value)\"\n />\n </div>\n }\n </div>\n } @empty {\n <p class=\"fb-empty\">\n Almeno un messaggio e\u2019 obbligatorio: sono il prodotto di questo elemento\n (CUSTOM_ERROR_WITHOUT_MESSAGES).\n </p>\n }\n</div>\n\n<button type=\"button\" class=\"fb-btn\" (click)=\"addMessage()\">Aggiungi messaggio</button>\n" }]
4858
+ args: [{ selector: 'fb-custom-error-inspector', standalone: true, changeDetection: ChangeDetectionStrategy.OnPush, template: "<p class=\"fb-callout\">\r\n Questo elemento <strong>interrompe</strong> l\u2019esecuzione e restituisce i messaggi al chiamante. Non ha\r\n uscite: quello che c\u2019e\u2019 dopo non verra\u2019 eseguito.\r\n</p>\r\n\r\n<div class=\"fb-list\">\r\n @for (message of messages(); 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 messaggio\"\r\n (click)=\"removeMessage($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 fb-field__label--required\">Messaggio</label>\r\n <textarea\r\n class=\"fb-textarea\"\r\n [value]=\"message.errorMessage || ''\"\r\n placeholder=\"L\u2019importo supera il limite consentito.\"\r\n (input)=\"setMessage($index, $any($event.target).value)\"\r\n ></textarea>\r\n </div>\r\n\r\n <label class=\"fb-check\">\r\n <input\r\n type=\"checkbox\"\r\n [checked]=\"message.isFieldError === true\"\r\n (change)=\"setIsFieldError($index, $any($event.target).checked)\"\r\n />\r\n Mostra accanto a un campo\r\n </label>\r\n\r\n @if (message.isFieldError) {\r\n <div class=\"fb-field\">\r\n <label class=\"fb-field__label fb-field__label--required\">Campo</label>\r\n <input\r\n class=\"fb-input\"\r\n [value]=\"message.fieldSelection || ''\"\r\n placeholder=\"Importo\"\r\n (input)=\"setFieldSelection($index, $any($event.target).value)\"\r\n />\r\n </div>\r\n }\r\n </div>\r\n } @empty {\r\n <p class=\"fb-empty\">\r\n Almeno un messaggio e\u2019 obbligatorio: sono il prodotto di questo elemento\r\n (CUSTOM_ERROR_WITHOUT_MESSAGES).\r\n </p>\r\n }\r\n</div>\r\n\r\n<button type=\"button\" class=\"fb-btn\" (click)=\"addMessage()\">Aggiungi messaggio</button>\r\n" }]
4461
4859
  }] });
4462
4860
 
4463
4861
  /**
@@ -4562,11 +4960,11 @@ class DecisionInspectorComponent extends NodeInspectorBase {
4562
4960
  this.setField('defaultConnectorLabel', label);
4563
4961
  }
4564
4962
  static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.3.27", ngImport: i0, type: DecisionInspectorComponent, deps: null, target: i0.ɵɵFactoryTarget.Component });
4565
- static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "20.3.27", type: DecisionInspectorComponent, isStandalone: true, selector: "fb-decision-inspector", usesInheritance: true, ngImport: i0, template: "<p class=\"fb-callout\">\n Le regole sono valutate <strong>dall\u2019alto verso il basso</strong> e si ferma alla prima vera. L\u2019ordine e\u2019\n parte del significato del flow: usa le frecce per cambiarlo.\n</p>\n\n<div class=\"fb-list\">\n @for (rule of rules(); track $index) {\n <div class=\"fb-list__item\">\n <div class=\"fb-list__header\">\n <span class=\"fb-list__index\" [title]=\"'Valutata per ' + ($index + 1) + '\u00AA'\">{{ $index + 1 }}</span>\n <span class=\"fb-list__spacer\"></span>\n <button\n type=\"button\"\n class=\"fb-btn fb-btn--ghost fb-btn--icon\"\n aria-label=\"Valuta prima\"\n [disabled]=\"$first\"\n (click)=\"moveRule($index, -1)\"\n >\n \u2191\n </button>\n <button\n type=\"button\"\n class=\"fb-btn fb-btn--ghost fb-btn--icon\"\n aria-label=\"Valuta dopo\"\n [disabled]=\"$last\"\n (click)=\"moveRule($index, 1)\"\n >\n \u2193\n </button>\n <button\n type=\"button\"\n class=\"fb-btn fb-btn--ghost fb-btn--icon\"\n aria-label=\"Rimuovi la regola\"\n (click)=\"removeRule($index)\"\n >\n \u00D7\n </button>\n </div>\n\n <div class=\"fb-field\">\n <label class=\"fb-field__label\">Etichetta del ramo</label>\n <input\n class=\"fb-input\"\n [value]=\"rule.label || ''\"\n placeholder=\"Approvato\"\n (input)=\"setRuleLabel($index, $any($event.target).value)\"\n />\n @if (!rule.label) {\n <p class=\"fb-field__hint\">Senza etichetta l\u2019arco resta senza nome sul canvas.</p>\n }\n </div>\n\n <div class=\"fb-field\">\n <label class=\"fb-field__label fb-field__label--required\">Nome tecnico</label>\n <input\n class=\"fb-input fb-input--mono\"\n [class.fb-input--invalid]=\"!!ruleNameError($index)\"\n [value]=\"rule.name || ''\"\n (input)=\"setRuleName($index, $any($event.target).value)\"\n />\n @if (ruleNameError($index)) {\n <p class=\"fb-field__error\">{{ ruleNameError($index) }}</p>\n }\n </div>\n\n <fb-condition-editor\n [holder]=\"rule\"\n title=\"Quando prendere questo ramo\"\n [issuePath]=\"'rules[' + (rule.name || $index) + ']'\"\n (changed)=\"onRuleConditionsChanged($index, $event)\"\n />\n </div>\n } @empty {\n <p class=\"fb-empty\">\n Nessuna regola: una Decision senza regole prende sempre il ramo di default\n (DECISION_WITHOUT_RULES).\n </p>\n }\n</div>\n\n<button type=\"button\" class=\"fb-btn\" (click)=\"addRule()\">Aggiungi regola</button>\n\n<fieldset class=\"fb-section\">\n <legend class=\"fb-section__title\">Se nessuna regola e\u2019 vera</legend>\n <div class=\"fb-field\">\n <label class=\"fb-field__label\">Etichetta del ramo di default</label>\n <input\n class=\"fb-input\"\n [value]=\"defaultLabel()\"\n placeholder=\"Rifiutato\"\n (input)=\"setDefaultLabel($any($event.target).value)\"\n />\n </div>\n @if (!hasDefaultTarget()) {\n <p class=\"fb-field__hint\">\n Nessuna destinazione di default: se nessuna regola e\u2019 vera l\u2019esecuzione finisce qui.\n </p>\n }\n</fieldset>\n\n<fb-connector-editor\n [nodeName]=\"name()\"\n [node]=\"node()\"\n [outlets]=\"outlets()\"\n title=\"Rami\"\n (connectorChanged)=\"onConnectorChanged($event)\"\n/>\n", dependencies: [{ kind: "component", type: ConditionEditorComponent, selector: "fb-condition-editor", inputs: ["holder", "title", "allowFormula", "issuePath"], outputs: ["changed"] }, { kind: "component", type: ConnectorEditorComponent, selector: "fb-connector-editor", inputs: ["nodeName", "node", "outlets", "title"], outputs: ["connectorChanged"] }], changeDetection: i0.ChangeDetectionStrategy.OnPush });
4963
+ static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "20.3.27", type: DecisionInspectorComponent, isStandalone: true, selector: "fb-decision-inspector", usesInheritance: true, ngImport: i0, template: "<p class=\"fb-callout\">\r\n Le regole sono valutate <strong>dall\u2019alto verso il basso</strong> e si ferma alla prima vera. L\u2019ordine e\u2019\r\n parte del significato del flow: usa le frecce per cambiarlo.\r\n</p>\r\n\r\n<div class=\"fb-list\">\r\n @for (rule of rules(); track $index) {\r\n <div class=\"fb-list__item\">\r\n <div class=\"fb-list__header\">\r\n <span class=\"fb-list__index\" [title]=\"'Valutata per ' + ($index + 1) + '\u00AA'\">{{ $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=\"Valuta prima\"\r\n [disabled]=\"$first\"\r\n (click)=\"moveRule($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=\"Valuta dopo\"\r\n [disabled]=\"$last\"\r\n (click)=\"moveRule($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 regola\"\r\n (click)=\"removeRule($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 del ramo</label>\r\n <input\r\n class=\"fb-input\"\r\n [value]=\"rule.label || ''\"\r\n placeholder=\"Approvato\"\r\n (input)=\"setRuleLabel($index, $any($event.target).value)\"\r\n />\r\n @if (!rule.label) {\r\n <p class=\"fb-field__hint\">Senza etichetta l\u2019arco resta senza nome sul canvas.</p>\r\n }\r\n </div>\r\n\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 [class.fb-input--invalid]=\"!!ruleNameError($index)\"\r\n [value]=\"rule.name || ''\"\r\n (input)=\"setRuleName($index, $any($event.target).value)\"\r\n />\r\n @if (ruleNameError($index)) {\r\n <p class=\"fb-field__error\">{{ ruleNameError($index) }}</p>\r\n }\r\n </div>\r\n\r\n <fb-condition-editor\r\n [holder]=\"rule\"\r\n title=\"Quando prendere questo ramo\"\r\n [issuePath]=\"'rules[' + (rule.name || $index) + ']'\"\r\n (changed)=\"onRuleConditionsChanged($index, $event)\"\r\n />\r\n </div>\r\n } @empty {\r\n <p class=\"fb-empty\">\r\n Nessuna regola: una Decision senza regole prende sempre il ramo di default\r\n (DECISION_WITHOUT_RULES).\r\n </p>\r\n }\r\n</div>\r\n\r\n<button type=\"button\" class=\"fb-btn\" (click)=\"addRule()\">Aggiungi regola</button>\r\n\r\n<fieldset class=\"fb-section\">\r\n <legend class=\"fb-section__title\">Se nessuna regola e\u2019 vera</legend>\r\n <div class=\"fb-field\">\r\n <label class=\"fb-field__label\">Etichetta del ramo di default</label>\r\n <input\r\n class=\"fb-input\"\r\n [value]=\"defaultLabel()\"\r\n placeholder=\"Rifiutato\"\r\n (input)=\"setDefaultLabel($any($event.target).value)\"\r\n />\r\n </div>\r\n @if (!hasDefaultTarget()) {\r\n <p class=\"fb-field__hint\">\r\n Nessuna destinazione di default: se nessuna regola e\u2019 vera l\u2019esecuzione finisce qui.\r\n </p>\r\n }\r\n</fieldset>\r\n\r\n<fb-connector-editor\r\n [nodeName]=\"name()\"\r\n [node]=\"node()\"\r\n [outlets]=\"outlets()\"\r\n title=\"Rami\"\r\n (connectorChanged)=\"onConnectorChanged($event)\"\r\n/>\r\n", dependencies: [{ kind: "component", type: ConditionEditorComponent, selector: "fb-condition-editor", inputs: ["holder", "title", "allowFormula", "allowLogic", "issuePath"], outputs: ["changed"] }, { kind: "component", type: ConnectorEditorComponent, selector: "fb-connector-editor", inputs: ["nodeName", "node", "outlets", "title"], outputs: ["connectorChanged"] }], changeDetection: i0.ChangeDetectionStrategy.OnPush });
4566
4964
  }
4567
4965
  i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.27", ngImport: i0, type: DecisionInspectorComponent, decorators: [{
4568
4966
  type: Component,
4569
- args: [{ selector: 'fb-decision-inspector', standalone: true, imports: [ConditionEditorComponent, ConnectorEditorComponent], changeDetection: ChangeDetectionStrategy.OnPush, template: "<p class=\"fb-callout\">\n Le regole sono valutate <strong>dall\u2019alto verso il basso</strong> e si ferma alla prima vera. L\u2019ordine e\u2019\n parte del significato del flow: usa le frecce per cambiarlo.\n</p>\n\n<div class=\"fb-list\">\n @for (rule of rules(); track $index) {\n <div class=\"fb-list__item\">\n <div class=\"fb-list__header\">\n <span class=\"fb-list__index\" [title]=\"'Valutata per ' + ($index + 1) + '\u00AA'\">{{ $index + 1 }}</span>\n <span class=\"fb-list__spacer\"></span>\n <button\n type=\"button\"\n class=\"fb-btn fb-btn--ghost fb-btn--icon\"\n aria-label=\"Valuta prima\"\n [disabled]=\"$first\"\n (click)=\"moveRule($index, -1)\"\n >\n \u2191\n </button>\n <button\n type=\"button\"\n class=\"fb-btn fb-btn--ghost fb-btn--icon\"\n aria-label=\"Valuta dopo\"\n [disabled]=\"$last\"\n (click)=\"moveRule($index, 1)\"\n >\n \u2193\n </button>\n <button\n type=\"button\"\n class=\"fb-btn fb-btn--ghost fb-btn--icon\"\n aria-label=\"Rimuovi la regola\"\n (click)=\"removeRule($index)\"\n >\n \u00D7\n </button>\n </div>\n\n <div class=\"fb-field\">\n <label class=\"fb-field__label\">Etichetta del ramo</label>\n <input\n class=\"fb-input\"\n [value]=\"rule.label || ''\"\n placeholder=\"Approvato\"\n (input)=\"setRuleLabel($index, $any($event.target).value)\"\n />\n @if (!rule.label) {\n <p class=\"fb-field__hint\">Senza etichetta l\u2019arco resta senza nome sul canvas.</p>\n }\n </div>\n\n <div class=\"fb-field\">\n <label class=\"fb-field__label fb-field__label--required\">Nome tecnico</label>\n <input\n class=\"fb-input fb-input--mono\"\n [class.fb-input--invalid]=\"!!ruleNameError($index)\"\n [value]=\"rule.name || ''\"\n (input)=\"setRuleName($index, $any($event.target).value)\"\n />\n @if (ruleNameError($index)) {\n <p class=\"fb-field__error\">{{ ruleNameError($index) }}</p>\n }\n </div>\n\n <fb-condition-editor\n [holder]=\"rule\"\n title=\"Quando prendere questo ramo\"\n [issuePath]=\"'rules[' + (rule.name || $index) + ']'\"\n (changed)=\"onRuleConditionsChanged($index, $event)\"\n />\n </div>\n } @empty {\n <p class=\"fb-empty\">\n Nessuna regola: una Decision senza regole prende sempre il ramo di default\n (DECISION_WITHOUT_RULES).\n </p>\n }\n</div>\n\n<button type=\"button\" class=\"fb-btn\" (click)=\"addRule()\">Aggiungi regola</button>\n\n<fieldset class=\"fb-section\">\n <legend class=\"fb-section__title\">Se nessuna regola e\u2019 vera</legend>\n <div class=\"fb-field\">\n <label class=\"fb-field__label\">Etichetta del ramo di default</label>\n <input\n class=\"fb-input\"\n [value]=\"defaultLabel()\"\n placeholder=\"Rifiutato\"\n (input)=\"setDefaultLabel($any($event.target).value)\"\n />\n </div>\n @if (!hasDefaultTarget()) {\n <p class=\"fb-field__hint\">\n Nessuna destinazione di default: se nessuna regola e\u2019 vera l\u2019esecuzione finisce qui.\n </p>\n }\n</fieldset>\n\n<fb-connector-editor\n [nodeName]=\"name()\"\n [node]=\"node()\"\n [outlets]=\"outlets()\"\n title=\"Rami\"\n (connectorChanged)=\"onConnectorChanged($event)\"\n/>\n" }]
4967
+ args: [{ selector: 'fb-decision-inspector', standalone: true, imports: [ConditionEditorComponent, ConnectorEditorComponent], changeDetection: ChangeDetectionStrategy.OnPush, template: "<p class=\"fb-callout\">\r\n Le regole sono valutate <strong>dall\u2019alto verso il basso</strong> e si ferma alla prima vera. L\u2019ordine e\u2019\r\n parte del significato del flow: usa le frecce per cambiarlo.\r\n</p>\r\n\r\n<div class=\"fb-list\">\r\n @for (rule of rules(); track $index) {\r\n <div class=\"fb-list__item\">\r\n <div class=\"fb-list__header\">\r\n <span class=\"fb-list__index\" [title]=\"'Valutata per ' + ($index + 1) + '\u00AA'\">{{ $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=\"Valuta prima\"\r\n [disabled]=\"$first\"\r\n (click)=\"moveRule($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=\"Valuta dopo\"\r\n [disabled]=\"$last\"\r\n (click)=\"moveRule($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 regola\"\r\n (click)=\"removeRule($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 del ramo</label>\r\n <input\r\n class=\"fb-input\"\r\n [value]=\"rule.label || ''\"\r\n placeholder=\"Approvato\"\r\n (input)=\"setRuleLabel($index, $any($event.target).value)\"\r\n />\r\n @if (!rule.label) {\r\n <p class=\"fb-field__hint\">Senza etichetta l\u2019arco resta senza nome sul canvas.</p>\r\n }\r\n </div>\r\n\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 [class.fb-input--invalid]=\"!!ruleNameError($index)\"\r\n [value]=\"rule.name || ''\"\r\n (input)=\"setRuleName($index, $any($event.target).value)\"\r\n />\r\n @if (ruleNameError($index)) {\r\n <p class=\"fb-field__error\">{{ ruleNameError($index) }}</p>\r\n }\r\n </div>\r\n\r\n <fb-condition-editor\r\n [holder]=\"rule\"\r\n title=\"Quando prendere questo ramo\"\r\n [issuePath]=\"'rules[' + (rule.name || $index) + ']'\"\r\n (changed)=\"onRuleConditionsChanged($index, $event)\"\r\n />\r\n </div>\r\n } @empty {\r\n <p class=\"fb-empty\">\r\n Nessuna regola: una Decision senza regole prende sempre il ramo di default\r\n (DECISION_WITHOUT_RULES).\r\n </p>\r\n }\r\n</div>\r\n\r\n<button type=\"button\" class=\"fb-btn\" (click)=\"addRule()\">Aggiungi regola</button>\r\n\r\n<fieldset class=\"fb-section\">\r\n <legend class=\"fb-section__title\">Se nessuna regola e\u2019 vera</legend>\r\n <div class=\"fb-field\">\r\n <label class=\"fb-field__label\">Etichetta del ramo di default</label>\r\n <input\r\n class=\"fb-input\"\r\n [value]=\"defaultLabel()\"\r\n placeholder=\"Rifiutato\"\r\n (input)=\"setDefaultLabel($any($event.target).value)\"\r\n />\r\n </div>\r\n @if (!hasDefaultTarget()) {\r\n <p class=\"fb-field__hint\">\r\n Nessuna destinazione di default: se nessuna regola e\u2019 vera l\u2019esecuzione finisce qui.\r\n </p>\r\n }\r\n</fieldset>\r\n\r\n<fb-connector-editor\r\n [nodeName]=\"name()\"\r\n [node]=\"node()\"\r\n [outlets]=\"outlets()\"\r\n title=\"Rami\"\r\n (connectorChanged)=\"onConnectorChanged($event)\"\r\n/>\r\n" }]
4570
4968
  }] });
4571
4969
 
4572
4970
  /**
@@ -4640,13 +5038,357 @@ class LoopInspectorComponent extends NodeInspectorBase {
4640
5038
  this.setField('iterationOrder', order);
4641
5039
  }
4642
5040
  static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.3.27", ngImport: i0, type: LoopInspectorComponent, deps: null, target: i0.ɵɵFactoryTarget.Component });
4643
- static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "20.3.27", type: LoopInspectorComponent, isStandalone: true, selector: "fb-loop-inspector", usesInheritance: true, ngImport: i0, template: "<div class=\"fb-field\">\r\n <label class=\"fb-field__label fb-field__label--required\">Collection da iterare</label>\r\n <!-- Solo collection: iterare una variabile singola e' un errore (LOOP_COLLECTION_MISSING). -->\r\n <fb-reference-picker\r\n [value]=\"loop().collectionReference\"\r\n [isCollection]=\"true\"\r\n placeholder=\"Scegli una collection\"\r\n (valueChange)=\"setCollection($event)\"\r\n />\r\n <p class=\"fb-field__hint\">\r\n Anche l\u2019output automatico di un Get Records senza \u00ABsolo il primo record\u00BB e\u2019 iterabile.\r\n </p>\r\n</div>\r\n\r\n<div class=\"fb-field\">\r\n <label class=\"fb-field__label fb-field__label--required\">Elemento corrente</label>\r\n <fb-reference-picker\r\n [value]=\"loop().assignNextValueToReference\"\r\n [writableOnly]=\"true\"\r\n [dataType]=\"collectionType()\"\r\n [objectType]=\"collectionObjectType()\"\r\n [isCollection]=\"false\"\r\n placeholder=\"Variabile che riceve l\u2019elemento\"\r\n (valueChange)=\"setCurrentValueVariable($event)\"\r\n />\r\n <p class=\"fb-field__hint\">\r\n Deve essere una variabile <strong>singola</strong> dello stesso tipo degli elementi: senza, il corpo del\r\n ciclo non ha modo di leggere l\u2019elemento corrente.\r\n </p>\r\n</div>\r\n\r\n<div class=\"fb-field\">\r\n <label class=\"fb-field__label\">Ordine di iterazione</label>\r\n <select\r\n class=\"fb-select\"\r\n [fbValue]=\"loop().iterationOrder || ''\"\r\n (change)=\"setIterationOrder($any($event.target).value)\"\r\n >\r\n <option value=\"\">\u2014 predefinito \u2014</option>\r\n @for (order of sortOrders(); track order.value) {\r\n <option [value]=\"order.value\">{{ order.label }}</option>\r\n }\r\n </select>\r\n</div>\r\n\r\n@if (hasBody() && !bodyReturnsToLoop()) {\r\n <p class=\"fb-callout fb-callout--warn\">\r\n Il corpo del ciclo non torna sul Loop: nessun percorso dal ramo \u00ABper ogni valore\u00BB rientra qui, quindi\r\n verrebbe eseguita una sola iterazione. Collega l\u2019ultimo elemento del corpo di nuovo al Loop, marcando\r\n l\u2019arco come salto indietro.\r\n </p>\r\n}\r\n@if (!hasBody()) {\r\n <p class=\"fb-callout fb-callout--error\">\r\n Il ramo \u00ABper ogni valore\u00BB non ha destinazione: il ciclo non ha corpo.\r\n </p>\r\n}\r\n\r\n<fb-connector-editor\r\n [nodeName]=\"name()\"\r\n [node]=\"node()\"\r\n [outlets]=\"outlets()\"\r\n title=\"Rami del ciclo\"\r\n (connectorChanged)=\"onConnectorChanged($event)\"\r\n/>\r\n\r\n<p class=\"fb-field__hint\">\r\n Il cursore si azzera all\u2019uscita: rientrare nello stesso Loop ricomincia da capo.\r\n</p>\r\n", dependencies: [{ kind: "component", type: ConnectorEditorComponent, selector: "fb-connector-editor", inputs: ["nodeName", "node", "outlets", "title"], outputs: ["connectorChanged"] }, { kind: "component", type: ReferencePickerComponent, selector: "fb-reference-picker", inputs: ["value", "label", "placeholder", "disabled", "dataType", "isCollection", "objectType", "writableOnly", "allowStageTargets", "elementsOnly"], outputs: ["valueChange"] }, { kind: "directive", type: SelectValueDirective, selector: "select[fbValue]", inputs: ["fbValue"] }], changeDetection: i0.ChangeDetectionStrategy.OnPush });
5041
+ static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "20.3.27", type: LoopInspectorComponent, isStandalone: true, selector: "fb-loop-inspector", usesInheritance: true, ngImport: i0, template: "<div class=\"fb-field\">\r\n <label class=\"fb-field__label fb-field__label--required\">Collection da iterare</label>\r\n <!-- Solo collection: iterare una variabile singola e' un errore (LOOP_COLLECTION_MISSING). -->\r\n <fb-reference-picker\r\n [value]=\"loop().collectionReference\"\r\n [isCollection]=\"true\"\r\n placeholder=\"Scegli una collection\"\r\n (valueChange)=\"setCollection($event)\"\r\n />\r\n <p class=\"fb-field__hint\">\r\n Anche l\u2019output automatico di un Get Records senza \u00ABsolo il primo record\u00BB e\u2019 iterabile.\r\n </p>\r\n</div>\r\n\r\n<div class=\"fb-field\">\r\n <label class=\"fb-field__label fb-field__label--required\">Elemento corrente</label>\r\n <fb-reference-picker\r\n [value]=\"loop().assignNextValueToReference\"\r\n [writableOnly]=\"true\"\r\n [dataType]=\"collectionType()\"\r\n [objectType]=\"collectionObjectType()\"\r\n [isCollection]=\"false\"\r\n placeholder=\"Variabile che riceve l\u2019elemento\"\r\n (valueChange)=\"setCurrentValueVariable($event)\"\r\n />\r\n <p class=\"fb-field__hint\">\r\n Deve essere una variabile <strong>singola</strong> dello stesso tipo degli elementi: senza, il corpo del\r\n ciclo non ha modo di leggere l\u2019elemento corrente.\r\n </p>\r\n</div>\r\n\r\n<div class=\"fb-field\">\r\n <label class=\"fb-field__label\">Ordine di iterazione</label>\r\n <select\r\n class=\"fb-select\"\r\n [fbValue]=\"loop().iterationOrder || ''\"\r\n (change)=\"setIterationOrder($any($event.target).value)\"\r\n >\r\n <option value=\"\">\u2014 predefinito \u2014</option>\r\n @for (order of sortOrders(); track order.value) {\r\n <option [value]=\"order.value\">{{ order.label }}</option>\r\n }\r\n </select>\r\n</div>\r\n\r\n@if (hasBody() && !bodyReturnsToLoop()) {\r\n <p class=\"fb-callout fb-callout--warn\">\r\n Il corpo del ciclo non torna sul Loop: nessun percorso dal ramo \u00ABper ogni valore\u00BB rientra qui, quindi\r\n verrebbe eseguita una sola iterazione. Collega l\u2019ultimo elemento del corpo di nuovo al Loop, marcando\r\n l\u2019arco come salto indietro.\r\n </p>\r\n}\r\n@if (!hasBody()) {\r\n <p class=\"fb-callout fb-callout--error\">\r\n Il ramo \u00ABper ogni valore\u00BB non ha destinazione: il ciclo non ha corpo.\r\n </p>\r\n}\r\n\r\n<fb-connector-editor\r\n [nodeName]=\"name()\"\r\n [node]=\"node()\"\r\n [outlets]=\"outlets()\"\r\n title=\"Rami del ciclo\"\r\n (connectorChanged)=\"onConnectorChanged($event)\"\r\n/>\r\n\r\n<p class=\"fb-field__hint\">\r\n Il cursore si azzera all\u2019uscita: rientrare nello stesso Loop ricomincia da capo.\r\n</p>\r\n", dependencies: [{ kind: "component", type: ConnectorEditorComponent, selector: "fb-connector-editor", inputs: ["nodeName", "node", "outlets", "title"], outputs: ["connectorChanged"] }, { kind: "component", type: ReferencePickerComponent, selector: "fb-reference-picker", inputs: ["value", "label", "placeholder", "disabled", "dataType", "isCollection", "objectType", "writableOnly", "elementsOnly"], outputs: ["valueChange"] }, { kind: "directive", type: SelectValueDirective, selector: "select[fbValue]", inputs: ["fbValue"] }], changeDetection: i0.ChangeDetectionStrategy.OnPush });
4644
5042
  }
4645
5043
  i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.27", ngImport: i0, type: LoopInspectorComponent, decorators: [{
4646
5044
  type: Component,
4647
5045
  args: [{ selector: 'fb-loop-inspector', standalone: true, imports: [ConnectorEditorComponent, ReferencePickerComponent, SelectValueDirective], changeDetection: ChangeDetectionStrategy.OnPush, template: "<div class=\"fb-field\">\r\n <label class=\"fb-field__label fb-field__label--required\">Collection da iterare</label>\r\n <!-- Solo collection: iterare una variabile singola e' un errore (LOOP_COLLECTION_MISSING). -->\r\n <fb-reference-picker\r\n [value]=\"loop().collectionReference\"\r\n [isCollection]=\"true\"\r\n placeholder=\"Scegli una collection\"\r\n (valueChange)=\"setCollection($event)\"\r\n />\r\n <p class=\"fb-field__hint\">\r\n Anche l\u2019output automatico di un Get Records senza \u00ABsolo il primo record\u00BB e\u2019 iterabile.\r\n </p>\r\n</div>\r\n\r\n<div class=\"fb-field\">\r\n <label class=\"fb-field__label fb-field__label--required\">Elemento corrente</label>\r\n <fb-reference-picker\r\n [value]=\"loop().assignNextValueToReference\"\r\n [writableOnly]=\"true\"\r\n [dataType]=\"collectionType()\"\r\n [objectType]=\"collectionObjectType()\"\r\n [isCollection]=\"false\"\r\n placeholder=\"Variabile che riceve l\u2019elemento\"\r\n (valueChange)=\"setCurrentValueVariable($event)\"\r\n />\r\n <p class=\"fb-field__hint\">\r\n Deve essere una variabile <strong>singola</strong> dello stesso tipo degli elementi: senza, il corpo del\r\n ciclo non ha modo di leggere l\u2019elemento corrente.\r\n </p>\r\n</div>\r\n\r\n<div class=\"fb-field\">\r\n <label class=\"fb-field__label\">Ordine di iterazione</label>\r\n <select\r\n class=\"fb-select\"\r\n [fbValue]=\"loop().iterationOrder || ''\"\r\n (change)=\"setIterationOrder($any($event.target).value)\"\r\n >\r\n <option value=\"\">\u2014 predefinito \u2014</option>\r\n @for (order of sortOrders(); track order.value) {\r\n <option [value]=\"order.value\">{{ order.label }}</option>\r\n }\r\n </select>\r\n</div>\r\n\r\n@if (hasBody() && !bodyReturnsToLoop()) {\r\n <p class=\"fb-callout fb-callout--warn\">\r\n Il corpo del ciclo non torna sul Loop: nessun percorso dal ramo \u00ABper ogni valore\u00BB rientra qui, quindi\r\n verrebbe eseguita una sola iterazione. Collega l\u2019ultimo elemento del corpo di nuovo al Loop, marcando\r\n l\u2019arco come salto indietro.\r\n </p>\r\n}\r\n@if (!hasBody()) {\r\n <p class=\"fb-callout fb-callout--error\">\r\n Il ramo \u00ABper ogni valore\u00BB non ha destinazione: il ciclo non ha corpo.\r\n </p>\r\n}\r\n\r\n<fb-connector-editor\r\n [nodeName]=\"name()\"\r\n [node]=\"node()\"\r\n [outlets]=\"outlets()\"\r\n title=\"Rami del ciclo\"\r\n (connectorChanged)=\"onConnectorChanged($event)\"\r\n/>\r\n\r\n<p class=\"fb-field__hint\">\r\n Il cursore si azzera all\u2019uscita: rientrare nello stesso Loop ricomincia da capo.\r\n</p>\r\n" }]
4648
5046
  }] });
4649
5047
 
5048
+ /**
5049
+ * Orchestrated Stage — FRONTEND.md §5.13.
5050
+ *
5051
+ * Un gruppo di step, alcuni affidati a una persona. Quello che questo form fa di diverso da un
5052
+ * elenco di sotto-elementi, e che il modello impone:
5053
+ *
5054
+ * 1. **Gli step non sono una sequenza.** L'ordine nell'array e' solo l'ordine d'esame: uno
5055
+ * step parte appena le sue `entryConditions` sono vere. Non sono numerati come passi, e le
5056
+ * frecce dicono di riordinare l'*esame*, non l'esecuzione.
5057
+ * 2. **Il `name` di uno step sta nello stesso spazio dei nomi** di node e risorse (§3.3): un
5058
+ * omonimo di una variabile e' `NAME_DUPLICATED`, e il controllo e' lo stesso dell'inspector.
5059
+ * 3. **Una condizione non puo' referenziare l'output di un altro step**: finche' quello step
5060
+ * non ha girato il riferimento e' irrisolvibile, e a runtime un riferimento irrisolvibile
5061
+ * e' un errore. Qui e' segnalato con la via d'uscita: scrivere il risultato in una
5062
+ * variabile e condizionare su quella.
5063
+ * 4. **`entryConditions` e `exitConditions` non sono simmetriche**: se le prime non si
5064
+ * avverano lo step viene saltato, se le seconde restano false lo stage va in **stallo** e
5065
+ * l'esecuzione fallisce. Sono due cose diverse che si assomigliano, quindi e' scritto.
5066
+ * 5. **`faultConnector` e' il ramo dello step rifiutato**, non un ramo di guasto: senza, un
5067
+ * rifiuto fa fallire l'interview. Per un flow di approvazione va quasi sempre disegnato.
5068
+ *
5069
+ * `runAsUser` viene ignorato dal runtime e `canAssigneeEdit` / `shouldLock` /
5070
+ * `requiresAsyncProcessing` sono riservati: non sono esposti.
5071
+ */
5072
+ class OrchestratedStageInspectorComponent extends NodeInspectorBase {
5073
+ api = inject(FlowBuilderApi);
5074
+ elementType = 'OrchestratedStage';
5075
+ stage = computed(() => this.node(), ...(ngDevMode ? [{ debugName: "stage" }] : []));
5076
+ steps = computed(() => stepsOf(this.stage()), ...(ngDevMode ? [{ debugName: "steps" }] : []));
5077
+ /** I tre tipi di step: `stageStepTypes` e' l'elenco autoritativo (§6.4). */
5078
+ stepTypes = computed(() => this.dictionaries.stageStepTypes(), ...(ngDevMode ? [{ debugName: "stepTypes" }] : []));
5079
+ assigneeTypes = computed(() => this.dictionaries.assigneeTypes(), ...(ngDevMode ? [{ debugName: "assigneeTypes" }] : []));
5080
+ /** L'output che il runtime legge da un evaluation flow di condizione. */
5081
+ conditionOutputName = ORCHESTRATION_CONDITION_OUTPUT;
5082
+ /**
5083
+ * I flow proponibili come step in background o come evaluation flow. Una lista vuota
5084
+ * significa "non lo so": in quel caso il campo resta scrivibile a mano (§5.9, §7).
5085
+ */
5086
+ flowCandidates = signal([], ...(ngDevMode ? [{ debugName: "flowCandidates" }] : []));
5087
+ constructor() {
5088
+ super();
5089
+ void this.api
5090
+ .listSubflowCandidates(this.store.document().fullName ?? undefined)
5091
+ .then((list) => this.flowCandidates.set(list ?? []))
5092
+ .catch(() => this.flowCandidates.set([]));
5093
+ }
5094
+ candidates = computed(() => this.flowCandidates(), ...(ngDevMode ? [{ debugName: "candidates" }] : []));
5095
+ /** L'id del `datalist`: dipende dall'elemento, per non collidere con un altro stage. */
5096
+ flowListId = computed(() => `fb-stage-flows-${this.name()}`, ...(ngDevMode ? [{ debugName: "flowListId" }] : []));
5097
+ /**
5098
+ * I contenitori di condizioni, uno per step, calcolati **una volta** per documento: costruirli
5099
+ * nel template creerebbe un oggetto nuovo a ogni ciclo di change detection.
5100
+ */
5101
+ conditionHolders = computed(() => this.steps().map((step) => ({
5102
+ entry: { conditions: step.entryConditions ?? [] },
5103
+ exit: { conditions: step.exitConditions ?? [] },
5104
+ })), ...(ngDevMode ? [{ debugName: "conditionHolders" }] : []));
5105
+ // -------------------------------------------------------------------------
5106
+ // Diagnostica locale
5107
+ // -------------------------------------------------------------------------
5108
+ requiresActionName(step) {
5109
+ return this.dictionaries.stageStepRequiresActionName(step.actionType);
5110
+ }
5111
+ requiresAssignees(step) {
5112
+ return this.dictionaries.stageStepRequiresAssignees(step.actionType);
5113
+ }
5114
+ /** Il rifiuto e' un esito previsto solo per l'approvazione: e' lì che serve il ramo. */
5115
+ supportsRejection(step) {
5116
+ const entry = this.dictionaries.stageStepType(step.actionType);
5117
+ return entry?.supportsRejection ?? step.actionType === 'stepApproval';
5118
+ }
5119
+ stepTypeDescription(step) {
5120
+ return this.dictionaries.stageStepType(step.actionType)?.description ?? null;
5121
+ }
5122
+ /** Un `actionType` che il dizionario non conosce: sarebbe `STAGE_STEP_TYPE_UNKNOWN`. */
5123
+ isUnknownStepType(step) {
5124
+ if (!step.actionType || !this.stepTypes().length) {
5125
+ return false;
5126
+ }
5127
+ return !this.dictionaries.stageStepType(step.actionType);
5128
+ }
5129
+ /**
5130
+ * Il nome dello step: stessa regola dei node, sullo spazio dei nomi comune (§3.3).
5131
+ * L'unicita' si conta sulle occorrenze in `usedNames`, che gli step li comprende già: una
5132
+ * sola occorrenza e' questo step, due sono un omonimo.
5133
+ */
5134
+ stepNameError(step) {
5135
+ const check = checkFlowName(step.name, [], step.name);
5136
+ if (!check.isValid) {
5137
+ return check.message ?? 'Nome non valido.';
5138
+ }
5139
+ const key = (step.name ?? '').toLowerCase();
5140
+ const occurrences = this.store.usedNames().filter((name) => name.toLowerCase() === key).length;
5141
+ return occurrences > 1
5142
+ ? 'Questo nome e’ già usato da un elemento, una risorsa o un altro step (NAME_DUPLICATED).'
5143
+ : null;
5144
+ }
5145
+ /**
5146
+ * Il nome dello step il cui output una condizione sta leggendo: e' irrisolvibile finche'
5147
+ * quello step non ha girato (§5.13, punto 4). `undefined` quando va tutto bene.
5148
+ */
5149
+ stepOutputInConditions(step) {
5150
+ const conditions = [...(step.entryConditions ?? []), ...(step.exitConditions ?? [])];
5151
+ for (const condition of conditions) {
5152
+ const referenced = stageStepOutputReferenced(condition.leftValueReference, this.steps()) ??
5153
+ stageStepOutputReferenced(condition.rightValue?.elementReference, this.steps());
5154
+ if (referenced) {
5155
+ return referenced;
5156
+ }
5157
+ }
5158
+ return undefined;
5159
+ }
5160
+ /** Lo step tiene un output senza destinazione: e' lecito, ma vale la pena dirlo. */
5161
+ hasSimulatedOutputs(step) {
5162
+ return step.debugSimulateStep === true;
5163
+ }
5164
+ // -------------------------------------------------------------------------
5165
+ // Step
5166
+ // -------------------------------------------------------------------------
5167
+ addStep() {
5168
+ const name = uniqueFlowName('Step', this.store.usedNames());
5169
+ this.patch((node) => {
5170
+ const stage = node;
5171
+ stage.stageSteps ??= [];
5172
+ stage.stageSteps.push({
5173
+ name,
5174
+ label: `Step ${stage.stageSteps.length + 1}`,
5175
+ // Il tipo lo scelga l'utente: senza, e' `STAGE_STEP_TYPE_MISSING` e si vede subito.
5176
+ });
5177
+ });
5178
+ }
5179
+ removeStep(index) {
5180
+ this.patch((node) => {
5181
+ const stage = node;
5182
+ stage.stageSteps?.splice(index, 1);
5183
+ if (stage.stageSteps?.length === 0) {
5184
+ delete stage.stageSteps;
5185
+ }
5186
+ });
5187
+ }
5188
+ /** Sposta lo step nell'ordine d'**esame**: non e' un ordine di esecuzione (§5.13). */
5189
+ moveStep(index, direction) {
5190
+ const target = index + direction;
5191
+ if (target < 0 || target >= this.steps().length) {
5192
+ return;
5193
+ }
5194
+ this.patch((node) => {
5195
+ const steps = node.stageSteps;
5196
+ if (!steps) {
5197
+ return;
5198
+ }
5199
+ const [moved] = steps.splice(index, 1);
5200
+ steps.splice(target, 0, moved);
5201
+ });
5202
+ }
5203
+ patchStep(index, mutate) {
5204
+ this.patch((node) => {
5205
+ const step = node.stageSteps?.[index];
5206
+ if (step) {
5207
+ mutate(step);
5208
+ }
5209
+ });
5210
+ }
5211
+ setStepLabel(index, label) {
5212
+ this.patchStep(index, (step) => {
5213
+ step.label = label || undefined;
5214
+ if (!step.name && label) {
5215
+ step.name = slugifyFlowName(label);
5216
+ }
5217
+ });
5218
+ }
5219
+ setStepName(index, name) {
5220
+ this.patchStep(index, (step) => {
5221
+ step.name = name || undefined;
5222
+ });
5223
+ }
5224
+ setStepDescription(index, description) {
5225
+ this.patchStep(index, (step) => {
5226
+ step.description = description || undefined;
5227
+ });
5228
+ }
5229
+ /**
5230
+ * Cambiare tipo cambia cosa lo step pretende: gli assegnatari non hanno senso su uno step in
5231
+ * background, e `actionName` non ne ha su uno assegnato. I campi che il nuovo tipo non
5232
+ * prevede si rimuovono, invece di restare nel documento a dire una cosa che non vale piu'.
5233
+ */
5234
+ setStepType(index, actionType) {
5235
+ const requiresActionName = this.dictionaries.stageStepRequiresActionName(actionType);
5236
+ const requiresAssignees = this.dictionaries.stageStepRequiresAssignees(actionType);
5237
+ this.patchStep(index, (step) => {
5238
+ step.actionType = actionType || undefined;
5239
+ if (!requiresActionName) {
5240
+ delete step.actionName;
5241
+ }
5242
+ if (!requiresAssignees) {
5243
+ delete step.assignees;
5244
+ delete step.requiresMultiMemberApproval;
5245
+ }
5246
+ else {
5247
+ step.assignees ??= [{}];
5248
+ }
5249
+ });
5250
+ }
5251
+ setStepActionName(index, actionName) {
5252
+ this.patchStep(index, (step) => {
5253
+ step.actionName = actionName || undefined;
5254
+ });
5255
+ }
5256
+ setMultiMemberApproval(index, required) {
5257
+ this.patchStep(index, (step) => {
5258
+ // Il modello lo porta come stringa: `"1"` oppure `"0"` (§5.13).
5259
+ step.requiresMultiMemberApproval = required ? '1' : '0';
5260
+ });
5261
+ }
5262
+ isMultiMemberApproval(step) {
5263
+ return step.requiresMultiMemberApproval === '1';
5264
+ }
5265
+ setSimulateStep(index, simulate) {
5266
+ this.patchStep(index, (step) => {
5267
+ if (simulate) {
5268
+ step.debugSimulateStep = true;
5269
+ }
5270
+ else {
5271
+ delete step.debugSimulateStep;
5272
+ delete step.outputConfigParams;
5273
+ }
5274
+ });
5275
+ }
5276
+ // -------------------------------------------------------------------------
5277
+ // Assegnatari
5278
+ // -------------------------------------------------------------------------
5279
+ assigneesOf(step) {
5280
+ return step.assignees ?? [];
5281
+ }
5282
+ addAssignee(index) {
5283
+ this.patchStep(index, (step) => {
5284
+ step.assignees ??= [];
5285
+ step.assignees.push({});
5286
+ });
5287
+ }
5288
+ removeAssignee(stepIndex, assigneeIndex) {
5289
+ this.patchStep(stepIndex, (step) => {
5290
+ step.assignees?.splice(assigneeIndex, 1);
5291
+ if (step.assignees?.length === 0) {
5292
+ delete step.assignees;
5293
+ }
5294
+ });
5295
+ }
5296
+ setAssigneeType(stepIndex, assigneeIndex, assigneeType) {
5297
+ this.patchStep(stepIndex, (step) => {
5298
+ const assignee = step.assignees?.[assigneeIndex];
5299
+ if (assignee) {
5300
+ assignee.assigneeType = assigneeType || undefined;
5301
+ }
5302
+ });
5303
+ }
5304
+ setAssigneeValue(stepIndex, assigneeIndex, value) {
5305
+ this.patchStep(stepIndex, (step) => {
5306
+ const assignee = step.assignees?.[assigneeIndex];
5307
+ if (assignee) {
5308
+ assignee.assignee = value;
5309
+ }
5310
+ });
5311
+ }
5312
+ /** Assegnatario senza tipo o senza valore: e' `STAGE_STEP_ASSIGNEE_INVALID`. */
5313
+ isAssigneeIncomplete(assignee) {
5314
+ return !assignee.assigneeType || !assignee.assignee;
5315
+ }
5316
+ // -------------------------------------------------------------------------
5317
+ // Condizioni ed evaluation flow
5318
+ // -------------------------------------------------------------------------
5319
+ /**
5320
+ * Le condizioni di uno step sono condizioni come le altre (§4.3), ma il modello porta solo
5321
+ * la lista: `conditionLogic` e `formula` non esistono e non vanno scritti.
5322
+ */
5323
+ onEntryConditionsChanged(index, mutate) {
5324
+ this.applyConditions(index, 'entryConditions', mutate);
5325
+ }
5326
+ onExitConditionsChanged(index, mutate) {
5327
+ this.applyConditions(index, 'exitConditions', mutate);
5328
+ }
5329
+ applyConditions(index, field, mutate) {
5330
+ this.patchStep(index, (step) => {
5331
+ const holder = { conditions: step[field] ?? [] };
5332
+ mutate(holder);
5333
+ const conditions = holder.conditions ?? [];
5334
+ if (conditions.length) {
5335
+ step[field] = conditions;
5336
+ }
5337
+ else {
5338
+ // Lista vuota omessa, non scritta come `[]` (§2).
5339
+ delete step[field];
5340
+ }
5341
+ });
5342
+ }
5343
+ setEntryActionName(index, actionName) {
5344
+ this.patchStep(index, (step) => {
5345
+ if (actionName) {
5346
+ step.entryActionName = actionName;
5347
+ // Il tipo e' implicito: la condizione la decide un evaluation flow (§5.13).
5348
+ step.entryActionType = 'EvaluationFlow';
5349
+ }
5350
+ else {
5351
+ delete step.entryActionName;
5352
+ delete step.entryActionType;
5353
+ }
5354
+ });
5355
+ }
5356
+ setExitActionName(index, actionName) {
5357
+ this.patchStep(index, (step) => {
5358
+ if (actionName) {
5359
+ step.exitActionName = actionName;
5360
+ step.exitActionType = 'EvaluationFlow';
5361
+ }
5362
+ else {
5363
+ delete step.exitActionName;
5364
+ delete step.exitActionType;
5365
+ }
5366
+ });
5367
+ }
5368
+ // -------------------------------------------------------------------------
5369
+ // Parametri
5370
+ // -------------------------------------------------------------------------
5371
+ onParametersChanged(index, mutate) {
5372
+ this.patchStep(index, (step) => mutate(step));
5373
+ }
5374
+ /** Il ramo del rifiuto e' disegnato: senza, un rifiuto fa fallire l'interview (§5.13). */
5375
+ hasRejectionBranch = computed(() => !!this.stage().faultConnector, ...(ngDevMode ? [{ debugName: "hasRejectionBranch" }] : []));
5376
+ /** C'e' almeno uno step il cui rifiuto e' un esito previsto. */
5377
+ hasApprovalStep = computed(() => this.steps().some((step) => this.supportsRejection(step)), ...(ngDevMode ? [{ debugName: "hasApprovalStep" }] : []));
5378
+ static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.3.27", ngImport: i0, type: OrchestratedStageInspectorComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
5379
+ static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "20.3.27", type: OrchestratedStageInspectorComponent, isStandalone: true, selector: "fb-orchestrated-stage-inspector", usesInheritance: true, ngImport: i0, template: "<fieldset class=\"fb-section\">\r\n <legend class=\"fb-section__title\">Step dello stage</legend>\r\n <p class=\"fb-section__note\">\r\n Gli step <strong>non sono una sequenza</strong>: parte quello le cui condizioni d\u2019ingresso sono vere, e\r\n l\u2019ordine qui sotto e\u2019 solo l\u2019ordine in cui vengono esaminati.\r\n </p>\r\n\r\n <div class=\"fb-list\">\r\n <!-- `stepIndex` esplicito: dentro l'elenco degli assegnatari `$index` e' quello dell'assegnatario. -->\r\n @for (step of steps(); track $index; let stepIndex = $index, isFirst = $first, isLast = $last) {\r\n <div class=\"fb-list__item\">\r\n <div class=\"fb-list__header\">\r\n <!-- Titolo, non indice: gli step non sono numerati perche' non sono una sequenza. -->\r\n <span class=\"fb-list__title\">{{ step.label || step.name || '\u2014' }}</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=\"Esamina prima\"\r\n title=\"Cambia l\u2019ordine d\u2019esame, non l\u2019ordine di esecuzione\"\r\n [disabled]=\"isFirst\"\r\n (click)=\"moveStep(stepIndex, -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=\"Esamina dopo\"\r\n title=\"Cambia l\u2019ordine d\u2019esame, non l\u2019ordine di esecuzione\"\r\n [disabled]=\"isLast\"\r\n (click)=\"moveStep(stepIndex, 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 lo step\"\r\n (click)=\"removeStep(stepIndex)\"\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 fb-field__label--required\">Etichetta</label>\r\n <input\r\n class=\"fb-input\"\r\n [value]=\"step.label || ''\"\r\n placeholder=\"Approva la pratica\"\r\n (input)=\"setStepLabel(stepIndex, $any($event.target).value)\"\r\n />\r\n </div>\r\n\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 [class.fb-input--invalid]=\"!!stepNameError(step)\"\r\n [value]=\"step.name || ''\"\r\n (input)=\"setStepName(stepIndex, $any($event.target).value)\"\r\n />\r\n @if (stepNameError(step)) {\r\n <p class=\"fb-field__error\">{{ stepNameError(step) }}</p>\r\n } @else {\r\n <p class=\"fb-field__hint\">\r\n Sta nello stesso spazio dei nomi di elementi e risorse. Gli output dello step sono\r\n referenziabili come <code>{{ step.name || 'NomeStep' }}.NomeOutput</code>.\r\n </p>\r\n }\r\n </div>\r\n\r\n <div class=\"fb-field\">\r\n <label class=\"fb-field__label fb-field__label--required\">Tipo di step</label>\r\n <select\r\n class=\"fb-select\"\r\n [class.fb-input--invalid]=\"!step.actionType || isUnknownStepType(step)\"\r\n [fbValue]=\"step.actionType || ''\"\r\n (change)=\"setStepType(stepIndex, $any($event.target).value)\"\r\n >\r\n <option value=\"\">\u2014 scegli \u2014</option>\r\n @for (type of stepTypes(); track type.value) {\r\n <option [value]=\"type.value\">{{ type.label }}</option>\r\n }\r\n </select>\r\n @if (!step.actionType) {\r\n <p class=\"fb-field__error\">Senza tipo lo step non e\u2019 valido (STAGE_STEP_TYPE_MISSING).</p>\r\n } @else if (isUnknownStepType(step)) {\r\n <p class=\"fb-field__error\">\r\n \u00AB{{ step.actionType }}\u00BB non e\u2019 un tipo di step di questo sistema (STAGE_STEP_TYPE_UNKNOWN).\r\n </p>\r\n } @else if (stepTypeDescription(step)) {\r\n <p class=\"fb-field__hint\">{{ stepTypeDescription(step) }}</p>\r\n }\r\n </div>\r\n\r\n @if (requiresActionName(step)) {\r\n <div class=\"fb-field\">\r\n <label class=\"fb-field__label fb-field__label--required\">Flow da eseguire</label>\r\n <!--\r\n Una lista di candidati vuota significa \"non lo so\", non \"nessuno\": il campo resta\r\n scrivibile a mano invece di bloccare l'utente (\u00A77).\r\n -->\r\n <input\r\n class=\"fb-input fb-input--mono\"\r\n [class.fb-input--invalid]=\"!step.actionName\"\r\n [value]=\"step.actionName || ''\"\r\n [attr.list]=\"flowListId()\"\r\n placeholder=\"Preparazione_Pratica\"\r\n (input)=\"setStepActionName(stepIndex, $any($event.target).value)\"\r\n />\r\n @if (!step.actionName) {\r\n <p class=\"fb-field__error\">\r\n Uno step in background esegue un flow: senza, e\u2019 STAGE_STEP_FLOW_MISSING.\r\n </p>\r\n } @else {\r\n <p class=\"fb-field__hint\">Il motore lo esegue subito, senza coinvolgere nessuno.</p>\r\n }\r\n </div>\r\n }\r\n\r\n @if (requiresAssignees(step)) {\r\n <fieldset class=\"fb-section\">\r\n <legend class=\"fb-section__title\">Assegnatari</legend>\r\n <p class=\"fb-section__note\">\r\n Su questo step l\u2019interview si <strong>sospende</strong>: resta aperto un work item finche\u2019\r\n una persona non lo conclude.\r\n </p>\r\n <div class=\"fb-list\">\r\n @for (assignee of assigneesOf(step); 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 l\u2019assegnatario\"\r\n (click)=\"removeAssignee(stepIndex, $index)\"\r\n >\r\n \u00D7\r\n </button>\r\n </div>\r\n <div class=\"fb-field\">\r\n <label class=\"fb-field__label fb-field__label--required\">Tipo</label>\r\n <select\r\n class=\"fb-select\"\r\n [fbValue]=\"assignee.assigneeType || ''\"\r\n (change)=\"setAssigneeType(stepIndex, $index, $any($event.target).value)\"\r\n >\r\n <option value=\"\">\u2014 scegli \u2014</option>\r\n @for (type of assigneeTypes(); track type.value) {\r\n <option [value]=\"type.value\">{{ type.label }}</option>\r\n }\r\n </select>\r\n </div>\r\n <div class=\"fb-field\">\r\n <label class=\"fb-field__label fb-field__label--required\">Chi</label>\r\n <fb-value-editor\r\n [value]=\"assignee.assignee\"\r\n label=\"Assegnatario\"\r\n dataType=\"String\"\r\n (valueChange)=\"setAssigneeValue(stepIndex, $index, $event)\"\r\n />\r\n </div>\r\n @if (isAssigneeIncomplete(assignee)) {\r\n <p class=\"fb-field__error\">Servono tipo e destinatario (STAGE_STEP_ASSIGNEE_INVALID).</p>\r\n }\r\n </div>\r\n } @empty {\r\n <p class=\"fb-empty\">\r\n Senza assegnatari questo step non e\u2019 valido (STAGE_STEP_ASSIGNEES_MISSING).\r\n </p>\r\n }\r\n </div>\r\n <button type=\"button\" class=\"fb-btn\" (click)=\"addAssignee(stepIndex)\">\r\n Aggiungi assegnatario\r\n </button>\r\n\r\n @if (supportsRejection(step)) {\r\n <label class=\"fb-check\">\r\n <input\r\n type=\"checkbox\"\r\n [checked]=\"isMultiMemberApproval(step)\"\r\n (change)=\"setMultiMemberApproval(stepIndex, $any($event.target).checked)\"\r\n />\r\n Serve l\u2019approvazione di tutti i membri\r\n </label>\r\n }\r\n </fieldset>\r\n }\r\n\r\n <!--\r\n Ingresso e uscita non sono simmetriche: se l'ingresso non si avvera lo step viene\r\n saltato, se l'uscita resta falsa lo stage va in stallo e l'esecuzione fallisce (\u00A75.13).\r\n -->\r\n <fb-condition-editor\r\n [holder]=\"conditionHolders()[stepIndex].entry\"\r\n title=\"Condizioni d\u2019ingresso (se lo step si applica)\"\r\n [allowLogic]=\"false\"\r\n [allowFormula]=\"false\"\r\n (changed)=\"onEntryConditionsChanged(stepIndex, $event)\"\r\n />\r\n <p class=\"fb-field__hint\">\r\n Se non si avverano, lo step viene <strong>saltato</strong> e lo stage prosegue.\r\n </p>\r\n\r\n <fb-condition-editor\r\n [holder]=\"conditionHolders()[stepIndex].exit\"\r\n title=\"Condizioni d\u2019uscita (quando lo step libera lo stage)\"\r\n [allowLogic]=\"false\"\r\n [allowFormula]=\"false\"\r\n (changed)=\"onExitConditionsChanged(stepIndex, $event)\"\r\n />\r\n <p class=\"fb-field__hint fb-field__hint--warn\">\r\n Se restano false quando non c\u2019e\u2019 piu\u2019 niente in esecuzione, lo stage e\u2019 in stallo e\r\n l\u2019esecuzione <strong>fallisce</strong>.\r\n </p>\r\n\r\n @if (stepOutputInConditions(step)) {\r\n <p class=\"fb-callout fb-callout--error\">\r\n Una condizione referenzia l\u2019output dello step \u00AB{{ stepOutputInConditions(step) }}\u00BB: finche\u2019\r\n quello step non ha girato il riferimento e\u2019 irrisolvibile, e a runtime e\u2019 un errore. Fai\r\n scrivere quel risultato in una variabile (parametro di uscita \u2192 destinazione) e condiziona su\r\n quella.\r\n </p>\r\n }\r\n\r\n <div class=\"fb-field\">\r\n <label class=\"fb-field__label\">Evaluation flow per l\u2019ingresso</label>\r\n <input\r\n class=\"fb-input fb-input--mono\"\r\n [value]=\"step.entryActionName || ''\"\r\n [attr.list]=\"flowListId()\"\r\n placeholder=\"Valuta_Ingresso\"\r\n (input)=\"setEntryActionName(stepIndex, $any($event.target).value)\"\r\n />\r\n <p class=\"fb-field__hint\">\r\n Deve restituire l\u2019output booleano <code>{{ conditionOutputName }}</code>: e\u2019 l\u2019unico che il\r\n runtime legge, dichiararne altri e\u2019 STAGE_ACTION_INVALID.\r\n </p>\r\n </div>\r\n\r\n <div class=\"fb-field\">\r\n <label class=\"fb-field__label\">Evaluation flow per l\u2019uscita</label>\r\n <input\r\n class=\"fb-input fb-input--mono\"\r\n [value]=\"step.exitActionName || ''\"\r\n [attr.list]=\"flowListId()\"\r\n placeholder=\"Valuta_Uscita\"\r\n (input)=\"setExitActionName(stepIndex, $any($event.target).value)\"\r\n />\r\n </div>\r\n\r\n <fb-parameter-editor\r\n [holder]=\"step\"\r\n inputTitle=\"Parametri dello step\"\r\n [showOutputs]=\"true\"\r\n outputTitle=\"Valori prodotti dallo step\"\r\n (changed)=\"onParametersChanged(stepIndex, $event)\"\r\n />\r\n <p class=\"fb-field__hint\">\r\n La destinazione e\u2019 <strong>facoltativa</strong>: l\u2019output e\u2019 gi\u00E0 referenziabile come\r\n <code>{{ step.name || 'NomeStep' }}.NomeOutput</code>. Serve una variabile solo se un altro step\r\n deve condizionare su quel risultato.\r\n </p>\r\n\r\n <label class=\"fb-check\">\r\n <input\r\n type=\"checkbox\"\r\n [checked]=\"hasSimulatedOutputs(step)\"\r\n (change)=\"setSimulateStep(stepIndex, $any($event.target).checked)\"\r\n />\r\n Simula lo step nella prova\r\n </label>\r\n @if (hasSimulatedOutputs(step)) {\r\n <p class=\"fb-callout fb-callout--warn\">\r\n Con la simulazione lo step non viene eseguito ne\u2019 assegnato: si usano gli output finti di\r\n <code>outputConfigParams</code>. Non memorizzarci dati personali.\r\n </p>\r\n }\r\n\r\n <div class=\"fb-field\">\r\n <label class=\"fb-field__label\">Descrizione</label>\r\n <textarea\r\n class=\"fb-textarea\"\r\n [value]=\"step.description || ''\"\r\n (input)=\"setStepDescription(stepIndex, $any($event.target).value)\"\r\n ></textarea>\r\n </div>\r\n </div>\r\n } @empty {\r\n <p class=\"fb-empty\">\r\n Uno stage senza step non fa nulla ed e\u2019 un errore di validazione (STAGE_WITHOUT_STEPS).\r\n </p>\r\n }\r\n </div>\r\n\r\n <button type=\"button\" class=\"fb-btn\" (click)=\"addStep()\">Aggiungi step</button>\r\n\r\n <!-- I candidati proposti a ogni campo che vuole il nome di un flow. -->\r\n <datalist [id]=\"flowListId()\">\r\n @for (candidate of candidates(); track candidate.flowName) {\r\n <option [value]=\"candidate.flowName\">{{ candidate.label || candidate.flowName }}</option>\r\n }\r\n </datalist>\r\n</fieldset>\r\n\r\n@if (hasApprovalStep() && !hasRejectionBranch()) {\r\n <p class=\"fb-callout fb-callout--warn\">\r\n C\u2019e\u2019 uno step di approvazione ma il ramo \u00ABStep rifiutato\u00BB non e\u2019 disegnato: senza, un rifiuto fa\r\n <strong>fallire</strong> l\u2019interview. Non e\u2019 un ramo di guasto, e\u2019 l\u2019esito previsto del rifiuto.\r\n </p>\r\n}\r\n\r\n<fb-connector-editor\r\n [nodeName]=\"name()\"\r\n [node]=\"node()\"\r\n [outlets]=\"outlets()\"\r\n title=\"Rami\"\r\n (connectorChanged)=\"onConnectorChanged($event)\"\r\n/>\r\n", dependencies: [{ kind: "component", type: ConditionEditorComponent, selector: "fb-condition-editor", inputs: ["holder", "title", "allowFormula", "allowLogic", "issuePath"], outputs: ["changed"] }, { kind: "component", type: ConnectorEditorComponent, selector: "fb-connector-editor", inputs: ["nodeName", "node", "outlets", "title"], outputs: ["connectorChanged"] }, { kind: "component", type: ParameterEditorComponent, selector: "fb-parameter-editor", inputs: ["holder", "catalogParameters", "inputTitle", "outputTitle", "showInputs", "showOutputs", "outputsDisabledReason"], outputs: ["changed"] }, { kind: "component", type: ValueEditorComponent, selector: "fb-value-editor", inputs: ["value", "label", "dataType", "objectType", "isCollection", "disabled", "allowFormula"], outputs: ["valueChange"] }, { kind: "directive", type: SelectValueDirective, selector: "select[fbValue]", inputs: ["fbValue"] }], changeDetection: i0.ChangeDetectionStrategy.OnPush });
5380
+ }
5381
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.27", ngImport: i0, type: OrchestratedStageInspectorComponent, decorators: [{
5382
+ type: Component,
5383
+ args: [{ selector: 'fb-orchestrated-stage-inspector', standalone: true, imports: [
5384
+ ConditionEditorComponent,
5385
+ ConnectorEditorComponent,
5386
+ ParameterEditorComponent,
5387
+ ValueEditorComponent,
5388
+ SelectValueDirective,
5389
+ ], changeDetection: ChangeDetectionStrategy.OnPush, template: "<fieldset class=\"fb-section\">\r\n <legend class=\"fb-section__title\">Step dello stage</legend>\r\n <p class=\"fb-section__note\">\r\n Gli step <strong>non sono una sequenza</strong>: parte quello le cui condizioni d\u2019ingresso sono vere, e\r\n l\u2019ordine qui sotto e\u2019 solo l\u2019ordine in cui vengono esaminati.\r\n </p>\r\n\r\n <div class=\"fb-list\">\r\n <!-- `stepIndex` esplicito: dentro l'elenco degli assegnatari `$index` e' quello dell'assegnatario. -->\r\n @for (step of steps(); track $index; let stepIndex = $index, isFirst = $first, isLast = $last) {\r\n <div class=\"fb-list__item\">\r\n <div class=\"fb-list__header\">\r\n <!-- Titolo, non indice: gli step non sono numerati perche' non sono una sequenza. -->\r\n <span class=\"fb-list__title\">{{ step.label || step.name || '\u2014' }}</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=\"Esamina prima\"\r\n title=\"Cambia l\u2019ordine d\u2019esame, non l\u2019ordine di esecuzione\"\r\n [disabled]=\"isFirst\"\r\n (click)=\"moveStep(stepIndex, -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=\"Esamina dopo\"\r\n title=\"Cambia l\u2019ordine d\u2019esame, non l\u2019ordine di esecuzione\"\r\n [disabled]=\"isLast\"\r\n (click)=\"moveStep(stepIndex, 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 lo step\"\r\n (click)=\"removeStep(stepIndex)\"\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 fb-field__label--required\">Etichetta</label>\r\n <input\r\n class=\"fb-input\"\r\n [value]=\"step.label || ''\"\r\n placeholder=\"Approva la pratica\"\r\n (input)=\"setStepLabel(stepIndex, $any($event.target).value)\"\r\n />\r\n </div>\r\n\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 [class.fb-input--invalid]=\"!!stepNameError(step)\"\r\n [value]=\"step.name || ''\"\r\n (input)=\"setStepName(stepIndex, $any($event.target).value)\"\r\n />\r\n @if (stepNameError(step)) {\r\n <p class=\"fb-field__error\">{{ stepNameError(step) }}</p>\r\n } @else {\r\n <p class=\"fb-field__hint\">\r\n Sta nello stesso spazio dei nomi di elementi e risorse. Gli output dello step sono\r\n referenziabili come <code>{{ step.name || 'NomeStep' }}.NomeOutput</code>.\r\n </p>\r\n }\r\n </div>\r\n\r\n <div class=\"fb-field\">\r\n <label class=\"fb-field__label fb-field__label--required\">Tipo di step</label>\r\n <select\r\n class=\"fb-select\"\r\n [class.fb-input--invalid]=\"!step.actionType || isUnknownStepType(step)\"\r\n [fbValue]=\"step.actionType || ''\"\r\n (change)=\"setStepType(stepIndex, $any($event.target).value)\"\r\n >\r\n <option value=\"\">\u2014 scegli \u2014</option>\r\n @for (type of stepTypes(); track type.value) {\r\n <option [value]=\"type.value\">{{ type.label }}</option>\r\n }\r\n </select>\r\n @if (!step.actionType) {\r\n <p class=\"fb-field__error\">Senza tipo lo step non e\u2019 valido (STAGE_STEP_TYPE_MISSING).</p>\r\n } @else if (isUnknownStepType(step)) {\r\n <p class=\"fb-field__error\">\r\n \u00AB{{ step.actionType }}\u00BB non e\u2019 un tipo di step di questo sistema (STAGE_STEP_TYPE_UNKNOWN).\r\n </p>\r\n } @else if (stepTypeDescription(step)) {\r\n <p class=\"fb-field__hint\">{{ stepTypeDescription(step) }}</p>\r\n }\r\n </div>\r\n\r\n @if (requiresActionName(step)) {\r\n <div class=\"fb-field\">\r\n <label class=\"fb-field__label fb-field__label--required\">Flow da eseguire</label>\r\n <!--\r\n Una lista di candidati vuota significa \"non lo so\", non \"nessuno\": il campo resta\r\n scrivibile a mano invece di bloccare l'utente (\u00A77).\r\n -->\r\n <input\r\n class=\"fb-input fb-input--mono\"\r\n [class.fb-input--invalid]=\"!step.actionName\"\r\n [value]=\"step.actionName || ''\"\r\n [attr.list]=\"flowListId()\"\r\n placeholder=\"Preparazione_Pratica\"\r\n (input)=\"setStepActionName(stepIndex, $any($event.target).value)\"\r\n />\r\n @if (!step.actionName) {\r\n <p class=\"fb-field__error\">\r\n Uno step in background esegue un flow: senza, e\u2019 STAGE_STEP_FLOW_MISSING.\r\n </p>\r\n } @else {\r\n <p class=\"fb-field__hint\">Il motore lo esegue subito, senza coinvolgere nessuno.</p>\r\n }\r\n </div>\r\n }\r\n\r\n @if (requiresAssignees(step)) {\r\n <fieldset class=\"fb-section\">\r\n <legend class=\"fb-section__title\">Assegnatari</legend>\r\n <p class=\"fb-section__note\">\r\n Su questo step l\u2019interview si <strong>sospende</strong>: resta aperto un work item finche\u2019\r\n una persona non lo conclude.\r\n </p>\r\n <div class=\"fb-list\">\r\n @for (assignee of assigneesOf(step); 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 l\u2019assegnatario\"\r\n (click)=\"removeAssignee(stepIndex, $index)\"\r\n >\r\n \u00D7\r\n </button>\r\n </div>\r\n <div class=\"fb-field\">\r\n <label class=\"fb-field__label fb-field__label--required\">Tipo</label>\r\n <select\r\n class=\"fb-select\"\r\n [fbValue]=\"assignee.assigneeType || ''\"\r\n (change)=\"setAssigneeType(stepIndex, $index, $any($event.target).value)\"\r\n >\r\n <option value=\"\">\u2014 scegli \u2014</option>\r\n @for (type of assigneeTypes(); track type.value) {\r\n <option [value]=\"type.value\">{{ type.label }}</option>\r\n }\r\n </select>\r\n </div>\r\n <div class=\"fb-field\">\r\n <label class=\"fb-field__label fb-field__label--required\">Chi</label>\r\n <fb-value-editor\r\n [value]=\"assignee.assignee\"\r\n label=\"Assegnatario\"\r\n dataType=\"String\"\r\n (valueChange)=\"setAssigneeValue(stepIndex, $index, $event)\"\r\n />\r\n </div>\r\n @if (isAssigneeIncomplete(assignee)) {\r\n <p class=\"fb-field__error\">Servono tipo e destinatario (STAGE_STEP_ASSIGNEE_INVALID).</p>\r\n }\r\n </div>\r\n } @empty {\r\n <p class=\"fb-empty\">\r\n Senza assegnatari questo step non e\u2019 valido (STAGE_STEP_ASSIGNEES_MISSING).\r\n </p>\r\n }\r\n </div>\r\n <button type=\"button\" class=\"fb-btn\" (click)=\"addAssignee(stepIndex)\">\r\n Aggiungi assegnatario\r\n </button>\r\n\r\n @if (supportsRejection(step)) {\r\n <label class=\"fb-check\">\r\n <input\r\n type=\"checkbox\"\r\n [checked]=\"isMultiMemberApproval(step)\"\r\n (change)=\"setMultiMemberApproval(stepIndex, $any($event.target).checked)\"\r\n />\r\n Serve l\u2019approvazione di tutti i membri\r\n </label>\r\n }\r\n </fieldset>\r\n }\r\n\r\n <!--\r\n Ingresso e uscita non sono simmetriche: se l'ingresso non si avvera lo step viene\r\n saltato, se l'uscita resta falsa lo stage va in stallo e l'esecuzione fallisce (\u00A75.13).\r\n -->\r\n <fb-condition-editor\r\n [holder]=\"conditionHolders()[stepIndex].entry\"\r\n title=\"Condizioni d\u2019ingresso (se lo step si applica)\"\r\n [allowLogic]=\"false\"\r\n [allowFormula]=\"false\"\r\n (changed)=\"onEntryConditionsChanged(stepIndex, $event)\"\r\n />\r\n <p class=\"fb-field__hint\">\r\n Se non si avverano, lo step viene <strong>saltato</strong> e lo stage prosegue.\r\n </p>\r\n\r\n <fb-condition-editor\r\n [holder]=\"conditionHolders()[stepIndex].exit\"\r\n title=\"Condizioni d\u2019uscita (quando lo step libera lo stage)\"\r\n [allowLogic]=\"false\"\r\n [allowFormula]=\"false\"\r\n (changed)=\"onExitConditionsChanged(stepIndex, $event)\"\r\n />\r\n <p class=\"fb-field__hint fb-field__hint--warn\">\r\n Se restano false quando non c\u2019e\u2019 piu\u2019 niente in esecuzione, lo stage e\u2019 in stallo e\r\n l\u2019esecuzione <strong>fallisce</strong>.\r\n </p>\r\n\r\n @if (stepOutputInConditions(step)) {\r\n <p class=\"fb-callout fb-callout--error\">\r\n Una condizione referenzia l\u2019output dello step \u00AB{{ stepOutputInConditions(step) }}\u00BB: finche\u2019\r\n quello step non ha girato il riferimento e\u2019 irrisolvibile, e a runtime e\u2019 un errore. Fai\r\n scrivere quel risultato in una variabile (parametro di uscita \u2192 destinazione) e condiziona su\r\n quella.\r\n </p>\r\n }\r\n\r\n <div class=\"fb-field\">\r\n <label class=\"fb-field__label\">Evaluation flow per l\u2019ingresso</label>\r\n <input\r\n class=\"fb-input fb-input--mono\"\r\n [value]=\"step.entryActionName || ''\"\r\n [attr.list]=\"flowListId()\"\r\n placeholder=\"Valuta_Ingresso\"\r\n (input)=\"setEntryActionName(stepIndex, $any($event.target).value)\"\r\n />\r\n <p class=\"fb-field__hint\">\r\n Deve restituire l\u2019output booleano <code>{{ conditionOutputName }}</code>: e\u2019 l\u2019unico che il\r\n runtime legge, dichiararne altri e\u2019 STAGE_ACTION_INVALID.\r\n </p>\r\n </div>\r\n\r\n <div class=\"fb-field\">\r\n <label class=\"fb-field__label\">Evaluation flow per l\u2019uscita</label>\r\n <input\r\n class=\"fb-input fb-input--mono\"\r\n [value]=\"step.exitActionName || ''\"\r\n [attr.list]=\"flowListId()\"\r\n placeholder=\"Valuta_Uscita\"\r\n (input)=\"setExitActionName(stepIndex, $any($event.target).value)\"\r\n />\r\n </div>\r\n\r\n <fb-parameter-editor\r\n [holder]=\"step\"\r\n inputTitle=\"Parametri dello step\"\r\n [showOutputs]=\"true\"\r\n outputTitle=\"Valori prodotti dallo step\"\r\n (changed)=\"onParametersChanged(stepIndex, $event)\"\r\n />\r\n <p class=\"fb-field__hint\">\r\n La destinazione e\u2019 <strong>facoltativa</strong>: l\u2019output e\u2019 gi\u00E0 referenziabile come\r\n <code>{{ step.name || 'NomeStep' }}.NomeOutput</code>. Serve una variabile solo se un altro step\r\n deve condizionare su quel risultato.\r\n </p>\r\n\r\n <label class=\"fb-check\">\r\n <input\r\n type=\"checkbox\"\r\n [checked]=\"hasSimulatedOutputs(step)\"\r\n (change)=\"setSimulateStep(stepIndex, $any($event.target).checked)\"\r\n />\r\n Simula lo step nella prova\r\n </label>\r\n @if (hasSimulatedOutputs(step)) {\r\n <p class=\"fb-callout fb-callout--warn\">\r\n Con la simulazione lo step non viene eseguito ne\u2019 assegnato: si usano gli output finti di\r\n <code>outputConfigParams</code>. Non memorizzarci dati personali.\r\n </p>\r\n }\r\n\r\n <div class=\"fb-field\">\r\n <label class=\"fb-field__label\">Descrizione</label>\r\n <textarea\r\n class=\"fb-textarea\"\r\n [value]=\"step.description || ''\"\r\n (input)=\"setStepDescription(stepIndex, $any($event.target).value)\"\r\n ></textarea>\r\n </div>\r\n </div>\r\n } @empty {\r\n <p class=\"fb-empty\">\r\n Uno stage senza step non fa nulla ed e\u2019 un errore di validazione (STAGE_WITHOUT_STEPS).\r\n </p>\r\n }\r\n </div>\r\n\r\n <button type=\"button\" class=\"fb-btn\" (click)=\"addStep()\">Aggiungi step</button>\r\n\r\n <!-- I candidati proposti a ogni campo che vuole il nome di un flow. -->\r\n <datalist [id]=\"flowListId()\">\r\n @for (candidate of candidates(); track candidate.flowName) {\r\n <option [value]=\"candidate.flowName\">{{ candidate.label || candidate.flowName }}</option>\r\n }\r\n </datalist>\r\n</fieldset>\r\n\r\n@if (hasApprovalStep() && !hasRejectionBranch()) {\r\n <p class=\"fb-callout fb-callout--warn\">\r\n C\u2019e\u2019 uno step di approvazione ma il ramo \u00ABStep rifiutato\u00BB non e\u2019 disegnato: senza, un rifiuto fa\r\n <strong>fallire</strong> l\u2019interview. Non e\u2019 un ramo di guasto, e\u2019 l\u2019esito previsto del rifiuto.\r\n </p>\r\n}\r\n\r\n<fb-connector-editor\r\n [nodeName]=\"name()\"\r\n [node]=\"node()\"\r\n [outlets]=\"outlets()\"\r\n title=\"Rami\"\r\n (connectorChanged)=\"onConnectorChanged($event)\"\r\n/>\r\n" }]
5390
+ }], ctorParameters: () => [] });
5391
+
4650
5392
  /**
4651
5393
  * Get Records — FRONTEND.md §5.6.
4652
5394
  *
@@ -4835,7 +5577,7 @@ class RecordLookupInspectorComponent extends NodeInspectorBase {
4835
5577
  /** `relatedRecords` e' modellato ma non tradotto in query: se c'e', si avvisa (§5.6). */
4836
5578
  hasRelatedRecords = computed(() => (this.lookup().relatedRecords?.length ?? 0) > 0, ...(ngDevMode ? [{ debugName: "hasRelatedRecords" }] : []));
4837
5579
  static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.3.27", ngImport: i0, type: RecordLookupInspectorComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
4838
- static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "20.3.27", type: RecordLookupInspectorComponent, isStandalone: true, selector: "fb-record-lookup-inspector", usesInheritance: true, ngImport: i0, template: "<div class=\"fb-field\">\r\n <label class=\"fb-field__label fb-field__label--required\">Oggetto</label>\r\n @if (hasObjectCatalog()) {\r\n <select class=\"fb-select\" [fbValue]=\"lookup().object || ''\" (change)=\"setObject($any($event.target).value)\">\r\n <option value=\"\">\u2014 scegli un oggetto \u2014</option>\r\n @for (object of objectOptions(); track object.name) {\r\n <option [value]=\"object.name\">{{ object.label || object.name }}</option>\r\n }\r\n </select>\r\n } @else {\r\n <input\r\n class=\"fb-input\"\r\n [value]=\"lookup().object || ''\"\r\n placeholder=\"Nome dell\u2019entita\u2019\"\r\n (input)=\"setObject($any($event.target).value)\"\r\n />\r\n }\r\n</div>\r\n\r\n<fb-record-filter-editor\r\n [holder]=\"lookup()\"\r\n [object]=\"lookup().object\"\r\n title=\"Quali record leggere\"\r\n usage=\"filterable\"\r\n [supportsLogic]=\"true\"\r\n [supportsFormula]=\"true\"\r\n emptyWarning=\"Senza filtri legge tutti i record dell\u2019oggetto.\"\r\n (changed)=\"onFiltersChanged($event)\"\r\n/>\r\n\r\n<fieldset class=\"fb-section\">\r\n <legend class=\"fb-section__title\">Quanti e in che ordine</legend>\r\n\r\n <label class=\"fb-check\">\r\n <input\r\n type=\"checkbox\"\r\n [checked]=\"lookup().getFirstRecordOnly === true\"\r\n (change)=\"setFirstOnly($any($event.target).checked)\"\r\n />\r\n Solo il primo record\r\n </label>\r\n <p class=\"fb-field__hint\">\r\n @if (returnsCollection()) {\r\n Il risultato e\u2019 una <strong>collection</strong>: puo\u2019 essere iterata da un Loop.\r\n } @else {\r\n Il risultato e\u2019 un <strong>record singolo</strong>: non e\u2019 iterabile da un Loop.\r\n }\r\n </p>\r\n\r\n <div class=\"fb-field\">\r\n <label class=\"fb-field__label\">Ordina per</label>\r\n <select class=\"fb-select\" [fbValue]=\"lookup().sortField || ''\" (change)=\"setSortField($any($event.target).value)\">\r\n <option value=\"\">\u2014 nessun ordinamento \u2014</option>\r\n @for (field of sortableOptions(); track field.name) {\r\n <option [value]=\"field.name\">{{ field.label || field.name }}</option>\r\n }\r\n </select>\r\n </div>\r\n\r\n @if (lookup().sortField) {\r\n <div class=\"fb-field\">\r\n <label class=\"fb-field__label\">Direzione</label>\r\n <select class=\"fb-select\" [fbValue]=\"lookup().sortOrder || ''\" (change)=\"setSortOrder($any($event.target).value)\">\r\n @for (order of sortOrders(); track order.value) {\r\n <option [value]=\"order.value\">{{ order.label }}</option>\r\n }\r\n </select>\r\n </div>\r\n }\r\n\r\n @if (returnsCollection()) {\r\n <div class=\"fb-field\">\r\n <label class=\"fb-field__label\">Numero massimo di record</label>\r\n <input\r\n class=\"fb-input\"\r\n type=\"number\"\r\n min=\"1\"\r\n [value]=\"lookup().limit ?? ''\"\r\n (input)=\"setLimit($any($event.target).value)\"\r\n />\r\n </div>\r\n }\r\n</fieldset>\r\n\r\n@if (fieldOptions().length) {\r\n <fieldset class=\"fb-section\">\r\n <legend class=\"fb-section__title\">Campi da leggere</legend>\r\n <p class=\"fb-section__note\">Nessuna selezione = tutti i campi disponibili.</p>\r\n <div class=\"fb-fields-grid\">\r\n @for (field of fieldOptions(); track field.name) {\r\n <label class=\"fb-check\">\r\n <input\r\n type=\"checkbox\"\r\n [checked]=\"isQueried(field.name)\"\r\n (change)=\"toggleQueriedField(field.name, $any($event.target).checked)\"\r\n />\r\n {{ field.label || field.name }}\r\n </label>\r\n }\r\n </div>\r\n </fieldset>\r\n}\r\n\r\n<fieldset class=\"fb-section\">\r\n <legend class=\"fb-section__title\">Dove finisce il risultato</legend>\r\n\r\n @if (hasOutputConflict()) {\r\n <p class=\"fb-callout fb-callout--error\">\r\n Sono dichiarati insieme l\u2019output automatico e una destinazione esplicita: e\u2019 un conflitto\r\n (OUTPUT_CONFIGURATION_CONFLICT). Scegli una sola modalita\u2019 qui sotto.\r\n </p>\r\n }\r\n\r\n <div class=\"fb-field\">\r\n <label class=\"fb-check\">\r\n <input\r\n type=\"radio\"\r\n name=\"output-mode\"\r\n [checked]=\"outputMode() === 'automatic'\"\r\n (change)=\"setOutputMode('automatic')\"\r\n />\r\n Output automatico <em>(consigliato)</em>\r\n </label>\r\n @if (outputMode() === 'automatic') {\r\n <p class=\"fb-field__hint\">\r\n Il risultato si referenzia con il nome dell\u2019elemento: <code>{{ name() }}</code>,\r\n <code>{{ name() }}.Campo</code>. Non serve dichiarare nessuna variabile.\r\n </p>\r\n }\r\n\r\n <label class=\"fb-check\">\r\n <input\r\n type=\"radio\"\r\n name=\"output-mode\"\r\n [checked]=\"outputMode() === 'variable'\"\r\n (change)=\"setOutputMode('variable')\"\r\n />\r\n In una variabile\r\n </label>\r\n <label class=\"fb-check\">\r\n <input\r\n type=\"radio\"\r\n name=\"output-mode\"\r\n [checked]=\"outputMode() === 'assignments'\"\r\n (change)=\"setOutputMode('assignments')\"\r\n />\r\n Campo per campo\r\n </label>\r\n <label class=\"fb-check\">\r\n <input\r\n type=\"radio\"\r\n name=\"output-mode\"\r\n [checked]=\"outputMode() === 'discard'\"\r\n (change)=\"setOutputMode('discard')\"\r\n />\r\n Scarta il risultato\r\n </label>\r\n </div>\r\n\r\n @if (outputMode() === 'variable') {\r\n <div class=\"fb-field\">\r\n <label class=\"fb-field__label fb-field__label--required\">Variabile di destinazione</label>\r\n <fb-reference-picker\r\n [value]=\"lookup().outputReference\"\r\n [writableOnly]=\"true\"\r\n [isCollection]=\"returnsCollection()\"\r\n [objectType]=\"lookup().object\"\r\n placeholder=\"Scegli una variabile\"\r\n (valueChange)=\"setOutputReference($event)\"\r\n />\r\n </div>\r\n }\r\n\r\n @if (outputMode() === 'assignments') {\r\n <div class=\"fb-list\">\r\n @for (assignment of outputAssignments(); 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\"\r\n (click)=\"removeOutputAssignment($index)\"\r\n >\r\n \u00D7\r\n </button>\r\n </div>\r\n <div class=\"fb-field\">\r\n <label class=\"fb-field__label\">Campo del record</label>\r\n <select\r\n class=\"fb-select\"\r\n [fbValue]=\"assignment.field || ''\"\r\n (change)=\"setOutputAssignmentField($index, $any($event.target).value)\"\r\n >\r\n <option value=\"\">\u2014 scegli \u2014</option>\r\n @for (field of fieldOptions(); track field.name) {\r\n <option [value]=\"field.name\">{{ field.label || field.name }}</option>\r\n }\r\n </select>\r\n </div>\r\n <div class=\"fb-field\">\r\n <label class=\"fb-field__label\">Assegna a</label>\r\n <fb-reference-picker\r\n [value]=\"assignment.assignToReference\"\r\n [writableOnly]=\"true\"\r\n placeholder=\"Scegli una variabile\"\r\n (valueChange)=\"setOutputAssignmentTarget($index, $event)\"\r\n />\r\n </div>\r\n </div>\r\n }\r\n </div>\r\n <button type=\"button\" class=\"fb-btn\" (click)=\"addOutputAssignment()\">Aggiungi campo</button>\r\n }\r\n\r\n @if (outputMode() === 'discard') {\r\n <p class=\"fb-callout fb-callout--warn\">\r\n L\u2019elemento interroga il database e butta via il risultato (LOOKUP_RESULT_DISCARDED).\r\n </p>\r\n }\r\n</fieldset>\r\n\r\n@if (hasRelatedRecords()) {\r\n <p class=\"fb-callout fb-callout--warn\">\r\n Questo elemento dichiara <code>relatedRecords</code>: il campo e\u2019 modellato ma non viene tradotto in\r\n query, quindi non ha effetto.\r\n </p>\r\n}\r\n\r\n<fb-connector-editor\r\n [nodeName]=\"name()\"\r\n [node]=\"node()\"\r\n [outlets]=\"outlets()\"\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: RecordFilterEditorComponent, selector: "fb-record-filter-editor", inputs: ["holder", "object", "title", "usage", "supportsLogic", "supportsFormula", "emptyWarning", "emptyWarningSeverity"], outputs: ["changed"] }, { kind: "component", type: ReferencePickerComponent, selector: "fb-reference-picker", inputs: ["value", "label", "placeholder", "disabled", "dataType", "isCollection", "objectType", "writableOnly", "allowStageTargets", "elementsOnly"], outputs: ["valueChange"] }, { kind: "directive", type: SelectValueDirective, selector: "select[fbValue]", inputs: ["fbValue"] }], changeDetection: i0.ChangeDetectionStrategy.OnPush });
5580
+ static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "20.3.27", type: RecordLookupInspectorComponent, isStandalone: true, selector: "fb-record-lookup-inspector", usesInheritance: true, ngImport: i0, template: "<div class=\"fb-field\">\r\n <label class=\"fb-field__label fb-field__label--required\">Oggetto</label>\r\n @if (hasObjectCatalog()) {\r\n <select class=\"fb-select\" [fbValue]=\"lookup().object || ''\" (change)=\"setObject($any($event.target).value)\">\r\n <option value=\"\">\u2014 scegli un oggetto \u2014</option>\r\n @for (object of objectOptions(); track object.name) {\r\n <option [value]=\"object.name\">{{ object.label || object.name }}</option>\r\n }\r\n </select>\r\n } @else {\r\n <input\r\n class=\"fb-input\"\r\n [value]=\"lookup().object || ''\"\r\n placeholder=\"Nome dell\u2019entita\u2019\"\r\n (input)=\"setObject($any($event.target).value)\"\r\n />\r\n }\r\n</div>\r\n\r\n<fb-record-filter-editor\r\n [holder]=\"lookup()\"\r\n [object]=\"lookup().object\"\r\n title=\"Quali record leggere\"\r\n usage=\"filterable\"\r\n [supportsLogic]=\"true\"\r\n [supportsFormula]=\"true\"\r\n emptyWarning=\"Senza filtri legge tutti i record dell\u2019oggetto.\"\r\n (changed)=\"onFiltersChanged($event)\"\r\n/>\r\n\r\n<fieldset class=\"fb-section\">\r\n <legend class=\"fb-section__title\">Quanti e in che ordine</legend>\r\n\r\n <label class=\"fb-check\">\r\n <input\r\n type=\"checkbox\"\r\n [checked]=\"lookup().getFirstRecordOnly === true\"\r\n (change)=\"setFirstOnly($any($event.target).checked)\"\r\n />\r\n Solo il primo record\r\n </label>\r\n <p class=\"fb-field__hint\">\r\n @if (returnsCollection()) {\r\n Il risultato e\u2019 una <strong>collection</strong>: puo\u2019 essere iterata da un Loop.\r\n } @else {\r\n Il risultato e\u2019 un <strong>record singolo</strong>: non e\u2019 iterabile da un Loop.\r\n }\r\n </p>\r\n\r\n <div class=\"fb-field\">\r\n <label class=\"fb-field__label\">Ordina per</label>\r\n <select class=\"fb-select\" [fbValue]=\"lookup().sortField || ''\" (change)=\"setSortField($any($event.target).value)\">\r\n <option value=\"\">\u2014 nessun ordinamento \u2014</option>\r\n @for (field of sortableOptions(); track field.name) {\r\n <option [value]=\"field.name\">{{ field.label || field.name }}</option>\r\n }\r\n </select>\r\n </div>\r\n\r\n @if (lookup().sortField) {\r\n <div class=\"fb-field\">\r\n <label class=\"fb-field__label\">Direzione</label>\r\n <select class=\"fb-select\" [fbValue]=\"lookup().sortOrder || ''\" (change)=\"setSortOrder($any($event.target).value)\">\r\n @for (order of sortOrders(); track order.value) {\r\n <option [value]=\"order.value\">{{ order.label }}</option>\r\n }\r\n </select>\r\n </div>\r\n }\r\n\r\n @if (returnsCollection()) {\r\n <div class=\"fb-field\">\r\n <label class=\"fb-field__label\">Numero massimo di record</label>\r\n <input\r\n class=\"fb-input\"\r\n type=\"number\"\r\n min=\"1\"\r\n [value]=\"lookup().limit ?? ''\"\r\n (input)=\"setLimit($any($event.target).value)\"\r\n />\r\n </div>\r\n }\r\n</fieldset>\r\n\r\n@if (fieldOptions().length) {\r\n <fieldset class=\"fb-section\">\r\n <legend class=\"fb-section__title\">Campi da leggere</legend>\r\n <p class=\"fb-section__note\">Nessuna selezione = tutti i campi disponibili.</p>\r\n <div class=\"fb-fields-grid\">\r\n @for (field of fieldOptions(); track field.name) {\r\n <label class=\"fb-check\">\r\n <input\r\n type=\"checkbox\"\r\n [checked]=\"isQueried(field.name)\"\r\n (change)=\"toggleQueriedField(field.name, $any($event.target).checked)\"\r\n />\r\n {{ field.label || field.name }}\r\n </label>\r\n }\r\n </div>\r\n </fieldset>\r\n}\r\n\r\n<fieldset class=\"fb-section\">\r\n <legend class=\"fb-section__title\">Dove finisce il risultato</legend>\r\n\r\n @if (hasOutputConflict()) {\r\n <p class=\"fb-callout fb-callout--error\">\r\n Sono dichiarati insieme l\u2019output automatico e una destinazione esplicita: e\u2019 un conflitto\r\n (OUTPUT_CONFIGURATION_CONFLICT). Scegli una sola modalita\u2019 qui sotto.\r\n </p>\r\n }\r\n\r\n <div class=\"fb-field\">\r\n <label class=\"fb-check\">\r\n <input\r\n type=\"radio\"\r\n name=\"output-mode\"\r\n [checked]=\"outputMode() === 'automatic'\"\r\n (change)=\"setOutputMode('automatic')\"\r\n />\r\n Output automatico <em>(consigliato)</em>\r\n </label>\r\n @if (outputMode() === 'automatic') {\r\n <p class=\"fb-field__hint\">\r\n Il risultato si referenzia con il nome dell\u2019elemento: <code>{{ name() }}</code>,\r\n <code>{{ name() }}.Campo</code>. Non serve dichiarare nessuna variabile.\r\n </p>\r\n }\r\n\r\n <label class=\"fb-check\">\r\n <input\r\n type=\"radio\"\r\n name=\"output-mode\"\r\n [checked]=\"outputMode() === 'variable'\"\r\n (change)=\"setOutputMode('variable')\"\r\n />\r\n In una variabile\r\n </label>\r\n <label class=\"fb-check\">\r\n <input\r\n type=\"radio\"\r\n name=\"output-mode\"\r\n [checked]=\"outputMode() === 'assignments'\"\r\n (change)=\"setOutputMode('assignments')\"\r\n />\r\n Campo per campo\r\n </label>\r\n <label class=\"fb-check\">\r\n <input\r\n type=\"radio\"\r\n name=\"output-mode\"\r\n [checked]=\"outputMode() === 'discard'\"\r\n (change)=\"setOutputMode('discard')\"\r\n />\r\n Scarta il risultato\r\n </label>\r\n </div>\r\n\r\n @if (outputMode() === 'variable') {\r\n <div class=\"fb-field\">\r\n <label class=\"fb-field__label fb-field__label--required\">Variabile di destinazione</label>\r\n <fb-reference-picker\r\n [value]=\"lookup().outputReference\"\r\n [writableOnly]=\"true\"\r\n [isCollection]=\"returnsCollection()\"\r\n [objectType]=\"lookup().object\"\r\n placeholder=\"Scegli una variabile\"\r\n (valueChange)=\"setOutputReference($event)\"\r\n />\r\n </div>\r\n }\r\n\r\n @if (outputMode() === 'assignments') {\r\n <div class=\"fb-list\">\r\n @for (assignment of outputAssignments(); 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\"\r\n (click)=\"removeOutputAssignment($index)\"\r\n >\r\n \u00D7\r\n </button>\r\n </div>\r\n <div class=\"fb-field\">\r\n <label class=\"fb-field__label\">Campo del record</label>\r\n <select\r\n class=\"fb-select\"\r\n [fbValue]=\"assignment.field || ''\"\r\n (change)=\"setOutputAssignmentField($index, $any($event.target).value)\"\r\n >\r\n <option value=\"\">\u2014 scegli \u2014</option>\r\n @for (field of fieldOptions(); track field.name) {\r\n <option [value]=\"field.name\">{{ field.label || field.name }}</option>\r\n }\r\n </select>\r\n </div>\r\n <div class=\"fb-field\">\r\n <label class=\"fb-field__label\">Assegna a</label>\r\n <fb-reference-picker\r\n [value]=\"assignment.assignToReference\"\r\n [writableOnly]=\"true\"\r\n placeholder=\"Scegli una variabile\"\r\n (valueChange)=\"setOutputAssignmentTarget($index, $event)\"\r\n />\r\n </div>\r\n </div>\r\n }\r\n </div>\r\n <button type=\"button\" class=\"fb-btn\" (click)=\"addOutputAssignment()\">Aggiungi campo</button>\r\n }\r\n\r\n @if (outputMode() === 'discard') {\r\n <p class=\"fb-callout fb-callout--warn\">\r\n L\u2019elemento interroga il database e butta via il risultato (LOOKUP_RESULT_DISCARDED).\r\n </p>\r\n }\r\n</fieldset>\r\n\r\n@if (hasRelatedRecords()) {\r\n <p class=\"fb-callout fb-callout--warn\">\r\n Questo elemento dichiara <code>relatedRecords</code>: il campo e\u2019 modellato ma non viene tradotto in\r\n query, quindi non ha effetto.\r\n </p>\r\n}\r\n\r\n<fb-connector-editor\r\n [nodeName]=\"name()\"\r\n [node]=\"node()\"\r\n [outlets]=\"outlets()\"\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: RecordFilterEditorComponent, selector: "fb-record-filter-editor", inputs: ["holder", "object", "title", "usage", "supportsLogic", "supportsFormula", "emptyWarning", "emptyWarningSeverity"], outputs: ["changed"] }, { kind: "component", type: ReferencePickerComponent, selector: "fb-reference-picker", inputs: ["value", "label", "placeholder", "disabled", "dataType", "isCollection", "objectType", "writableOnly", "elementsOnly"], outputs: ["valueChange"] }, { kind: "directive", type: SelectValueDirective, selector: "select[fbValue]", inputs: ["fbValue"] }], changeDetection: i0.ChangeDetectionStrategy.OnPush });
4839
5581
  }
4840
5582
  i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.27", ngImport: i0, type: RecordLookupInspectorComponent, decorators: [{
4841
5583
  type: Component,
@@ -4854,11 +5596,11 @@ class RecordRollbackInspectorComponent extends NodeInspectorBase {
4854
5596
  /** Fuori da uno screen flow non c'e' una transazione aperta da annullare. */
4855
5597
  isScreenFlow = computed(() => this.store.document().processType === 'Screen', ...(ngDevMode ? [{ debugName: "isScreenFlow" }] : []));
4856
5598
  static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.3.27", ngImport: i0, type: RecordRollbackInspectorComponent, deps: null, target: i0.ɵɵFactoryTarget.Component });
4857
- static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "20.3.27", type: RecordRollbackInspectorComponent, isStandalone: true, selector: "fb-record-rollback-inspector", usesInheritance: true, ngImport: i0, template: "<p class=\"fb-callout\">\n Annulla le modifiche ai record ancora pendenti. Non ha altri campi da configurare.\n</p>\n\n@if (!isScreenFlow()) {\n <p class=\"fb-callout fb-callout--warn\">\n Questo elemento ha senso in uno screen flow, dove esiste una transazione aperta fra due schermate. In un\n flow \u00AB{{ store.document().processType }}\u00BB non c\u2019e\u2019 nulla da annullare.\n </p>\n}\n\n<fb-connector-editor\n [nodeName]=\"name()\"\n [node]=\"node()\"\n [outlets]=\"outlets()\"\n (connectorChanged)=\"onConnectorChanged($event)\"\n/>\n", dependencies: [{ kind: "component", type: ConnectorEditorComponent, selector: "fb-connector-editor", inputs: ["nodeName", "node", "outlets", "title"], outputs: ["connectorChanged"] }], changeDetection: i0.ChangeDetectionStrategy.OnPush });
5599
+ static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "20.3.27", type: RecordRollbackInspectorComponent, isStandalone: true, selector: "fb-record-rollback-inspector", usesInheritance: true, ngImport: i0, template: "<p class=\"fb-callout\">\r\n Annulla le modifiche ai record ancora pendenti. Non ha altri campi da configurare.\r\n</p>\r\n\r\n@if (!isScreenFlow()) {\r\n <p class=\"fb-callout fb-callout--warn\">\r\n Questo elemento ha senso in uno screen flow, dove esiste una transazione aperta fra due schermate. In un\r\n flow \u00AB{{ store.document().processType }}\u00BB non c\u2019e\u2019 nulla da annullare.\r\n </p>\r\n}\r\n\r\n<fb-connector-editor\r\n [nodeName]=\"name()\"\r\n [node]=\"node()\"\r\n [outlets]=\"outlets()\"\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"] }], changeDetection: i0.ChangeDetectionStrategy.OnPush });
4858
5600
  }
4859
5601
  i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.27", ngImport: i0, type: RecordRollbackInspectorComponent, decorators: [{
4860
5602
  type: Component,
4861
- args: [{ selector: 'fb-record-rollback-inspector', standalone: true, imports: [ConnectorEditorComponent], changeDetection: ChangeDetectionStrategy.OnPush, template: "<p class=\"fb-callout\">\n Annulla le modifiche ai record ancora pendenti. Non ha altri campi da configurare.\n</p>\n\n@if (!isScreenFlow()) {\n <p class=\"fb-callout fb-callout--warn\">\n Questo elemento ha senso in uno screen flow, dove esiste una transazione aperta fra due schermate. In un\n flow \u00AB{{ store.document().processType }}\u00BB non c\u2019e\u2019 nulla da annullare.\n </p>\n}\n\n<fb-connector-editor\n [nodeName]=\"name()\"\n [node]=\"node()\"\n [outlets]=\"outlets()\"\n (connectorChanged)=\"onConnectorChanged($event)\"\n/>\n" }]
5603
+ args: [{ selector: 'fb-record-rollback-inspector', standalone: true, imports: [ConnectorEditorComponent], changeDetection: ChangeDetectionStrategy.OnPush, template: "<p class=\"fb-callout\">\r\n Annulla le modifiche ai record ancora pendenti. Non ha altri campi da configurare.\r\n</p>\r\n\r\n@if (!isScreenFlow()) {\r\n <p class=\"fb-callout fb-callout--warn\">\r\n Questo elemento ha senso in uno screen flow, dove esiste una transazione aperta fra due schermate. In un\r\n flow \u00AB{{ store.document().processType }}\u00BB non c\u2019e\u2019 nulla da annullare.\r\n </p>\r\n}\r\n\r\n<fb-connector-editor\r\n [nodeName]=\"name()\"\r\n [node]=\"node()\"\r\n [outlets]=\"outlets()\"\r\n (connectorChanged)=\"onConnectorChanged($event)\"\r\n/>\r\n" }]
4862
5604
  }] });
4863
5605
 
4864
5606
  /**
@@ -5038,7 +5780,7 @@ class RecordWriteInspectorComponent extends NodeInspectorBase {
5038
5780
  this.patch((node) => mutate(node));
5039
5781
  }
5040
5782
  static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.3.27", ngImport: i0, type: RecordWriteInspectorComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
5041
- static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "20.3.27", type: RecordWriteInspectorComponent, isStandalone: true, selector: "fb-record-write-inspector", inputs: { type: { classPropertyName: "type", publicName: "type", isSignal: true, isRequired: true, transformFunction: null } }, usesInheritance: true, ngImport: i0, template: "<fieldset class=\"fb-section\">\r\n <legend class=\"fb-section__title\">Come indicare i {{ title() }}</legend>\r\n <label class=\"fb-check\">\r\n <input type=\"radio\" name=\"write-mode\" [checked]=\"mode() === 'object'\" (change)=\"setMode('object')\" />\r\n Per oggetto{{ needsFilters() ? ' e filtri' : ' e valori' }}\r\n </label>\r\n <label class=\"fb-check\">\r\n <input type=\"radio\" name=\"write-mode\" [checked]=\"mode() === 'reference'\" (change)=\"setMode('reference')\" />\r\n Un record gi\u00E0 in memoria\r\n </label>\r\n</fieldset>\r\n\r\n@if (mode() === 'reference') {\r\n <div class=\"fb-field\">\r\n <label class=\"fb-field__label fb-field__label--required\">Record</label>\r\n <fb-reference-picker\r\n [value]=\"record().inputReference\"\r\n dataType=\"Object\"\r\n placeholder=\"Variabile di tipo record\"\r\n (valueChange)=\"setInputReference($event)\"\r\n />\r\n <p class=\"fb-field__hint\">\r\n Il record porta con se\u2019 i propri valori: non serve indicare oggetto ne\u2019 campi.\r\n </p>\r\n </div>\r\n} @else {\r\n <div class=\"fb-field\">\r\n <label class=\"fb-field__label fb-field__label--required\">Oggetto</label>\r\n @if (hasObjectCatalog()) {\r\n <select class=\"fb-select\" [fbValue]=\"record().object || ''\" (change)=\"setObject($any($event.target).value)\">\r\n <option value=\"\">\u2014 scegli un oggetto \u2014</option>\r\n @for (object of objectOptions(); track object.name) {\r\n <option [value]=\"object.name\">{{ object.label || object.name }}</option>\r\n }\r\n </select>\r\n } @else {\r\n <input\r\n class=\"fb-input\"\r\n [value]=\"record().object || ''\"\r\n placeholder=\"Nome dell\u2019entita\u2019\"\r\n (input)=\"setObject($any($event.target).value)\"\r\n />\r\n }\r\n </div>\r\n\r\n @if (needsFilters()) {\r\n <fb-record-filter-editor\r\n [holder]=\"$any(record())\"\r\n [object]=\"record().object\"\r\n [title]=\"isDelete() ? 'Quali record cancellare' : 'Quali record aggiornare'\"\r\n usage=\"filterable\"\r\n [supportsLogic]=\"supportsFilterLogic()\"\r\n [emptyWarning]=\"emptyFilterWarning()\"\r\n [emptyWarningSeverity]=\"emptyFilterSeverity()\"\r\n (changed)=\"onFiltersChanged($event)\"\r\n />\r\n\r\n @if (showsBulkConfirm()) {\r\n <label class=\"fb-check fb-confirm\">\r\n <input\r\n type=\"checkbox\"\r\n [checked]=\"bulkUpdateConfirmed()\"\r\n (change)=\"confirmBulkUpdate($any($event.target).checked)\"\r\n />\r\n Confermo di voler aggiornare <strong>tutti</strong> i record di \u00AB{{ record().object || 'questo oggetto' }}\u00BB\r\n </label>\r\n }\r\n }\r\n\r\n @if (!isDelete()) {\r\n <fb-field-assignment-editor\r\n [holder]=\"$any(record())\"\r\n [object]=\"record().object\"\r\n [title]=\"isCreate() ? 'Valori del nuovo record' : 'Valori da scrivere'\"\r\n (changed)=\"onAssignmentsChanged($event)\"\r\n />\r\n }\r\n}\r\n\r\n@if (isCreate()) {\r\n <fieldset class=\"fb-section\">\r\n <legend class=\"fb-section__title\">Upsert</legend>\r\n <label class=\"fb-check\">\r\n <input\r\n type=\"checkbox\"\r\n [checked]=\"record().doesUpsert === true\"\r\n (change)=\"setDoesUpsert($any($event.target).checked)\"\r\n />\r\n Aggiorna il record se esiste gi\u00E0\r\n </label>\r\n\r\n @if (record().doesUpsert) {\r\n @if (hasUpsertConflict()) {\r\n <p class=\"fb-callout fb-callout--error\">\r\n Sono indicati insieme il campo di id esterno e quello standard: va scelto uno solo\r\n (UPSERT_CONFIGURATION_INVALID).\r\n </p>\r\n }\r\n <label class=\"fb-check\">\r\n <input\r\n type=\"radio\"\r\n name=\"upsert-mode\"\r\n [checked]=\"upsertMode() === 'external'\"\r\n (change)=\"setUpsertMode('external')\"\r\n />\r\n Riconosci il record da un id esterno\r\n </label>\r\n <label class=\"fb-check\">\r\n <input\r\n type=\"radio\"\r\n name=\"upsert-mode\"\r\n [checked]=\"upsertMode() === 'standard'\"\r\n (change)=\"setUpsertMode('standard')\"\r\n />\r\n Riconosci il record dall\u2019id standard\r\n </label>\r\n\r\n @if (upsertMode() === 'external') {\r\n <div class=\"fb-field\">\r\n <label class=\"fb-field__label fb-field__label--required\">Campo id esterno</label>\r\n <input\r\n class=\"fb-input\"\r\n [value]=\"record().upsertExternalIdField || ''\"\r\n (input)=\"setUpsertExternalField($any($event.target).value)\"\r\n />\r\n </div>\r\n } @else {\r\n <div class=\"fb-field\">\r\n <label class=\"fb-field__label fb-field__label--required\">Campo id standard</label>\r\n <input\r\n class=\"fb-input\"\r\n [value]=\"record().upsertStandardIdField || ''\"\r\n (input)=\"setUpsertStandardField($any($event.target).value)\"\r\n />\r\n </div>\r\n }\r\n }\r\n </fieldset>\r\n\r\n <fieldset class=\"fb-section\">\r\n <legend class=\"fb-section__title\">Identificativo creato</legend>\r\n <label class=\"fb-check\">\r\n <input\r\n type=\"checkbox\"\r\n [checked]=\"record().storeOutputAutomatically === true\"\r\n (change)=\"setStoreOutputAutomatically($any($event.target).checked)\"\r\n />\r\n Output automatico\r\n </label>\r\n @if (record().storeOutputAutomatically) {\r\n <p class=\"fb-field__hint\">\r\n L\u2019identificativo creato si referenzia col nome dell\u2019elemento: <code>{{ name() }}</code>.\r\n </p>\r\n } @else {\r\n <div class=\"fb-field\">\r\n <label class=\"fb-field__label\">Assegna l\u2019identificativo a</label>\r\n <fb-reference-picker\r\n [value]=\"record().assignRecordIdToReference\"\r\n [writableOnly]=\"true\"\r\n placeholder=\"Scegli una variabile\"\r\n (valueChange)=\"setAssignRecordId($event)\"\r\n />\r\n </div>\r\n }\r\n </fieldset>\r\n}\r\n\r\n<fb-connector-editor\r\n [nodeName]=\"name()\"\r\n [node]=\"node()\"\r\n [outlets]=\"outlets()\"\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: FieldAssignmentEditorComponent, selector: "fb-field-assignment-editor", inputs: ["holder", "object", "title", "disabledReason"], outputs: ["changed"] }, { kind: "component", type: RecordFilterEditorComponent, selector: "fb-record-filter-editor", inputs: ["holder", "object", "title", "usage", "supportsLogic", "supportsFormula", "emptyWarning", "emptyWarningSeverity"], outputs: ["changed"] }, { kind: "component", type: ReferencePickerComponent, selector: "fb-reference-picker", inputs: ["value", "label", "placeholder", "disabled", "dataType", "isCollection", "objectType", "writableOnly", "allowStageTargets", "elementsOnly"], outputs: ["valueChange"] }, { kind: "directive", type: SelectValueDirective, selector: "select[fbValue]", inputs: ["fbValue"] }], changeDetection: i0.ChangeDetectionStrategy.OnPush });
5783
+ static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "20.3.27", type: RecordWriteInspectorComponent, isStandalone: true, selector: "fb-record-write-inspector", inputs: { type: { classPropertyName: "type", publicName: "type", isSignal: true, isRequired: true, transformFunction: null } }, usesInheritance: true, ngImport: i0, template: "<fieldset class=\"fb-section\">\r\n <legend class=\"fb-section__title\">Come indicare i {{ title() }}</legend>\r\n <label class=\"fb-check\">\r\n <input type=\"radio\" name=\"write-mode\" [checked]=\"mode() === 'object'\" (change)=\"setMode('object')\" />\r\n Per oggetto{{ needsFilters() ? ' e filtri' : ' e valori' }}\r\n </label>\r\n <label class=\"fb-check\">\r\n <input type=\"radio\" name=\"write-mode\" [checked]=\"mode() === 'reference'\" (change)=\"setMode('reference')\" />\r\n Un record gi\u00E0 in memoria\r\n </label>\r\n</fieldset>\r\n\r\n@if (mode() === 'reference') {\r\n <div class=\"fb-field\">\r\n <label class=\"fb-field__label fb-field__label--required\">Record</label>\r\n <fb-reference-picker\r\n [value]=\"record().inputReference\"\r\n dataType=\"Object\"\r\n placeholder=\"Variabile di tipo record\"\r\n (valueChange)=\"setInputReference($event)\"\r\n />\r\n <p class=\"fb-field__hint\">\r\n Il record porta con se\u2019 i propri valori: non serve indicare oggetto ne\u2019 campi.\r\n </p>\r\n </div>\r\n} @else {\r\n <div class=\"fb-field\">\r\n <label class=\"fb-field__label fb-field__label--required\">Oggetto</label>\r\n @if (hasObjectCatalog()) {\r\n <select class=\"fb-select\" [fbValue]=\"record().object || ''\" (change)=\"setObject($any($event.target).value)\">\r\n <option value=\"\">\u2014 scegli un oggetto \u2014</option>\r\n @for (object of objectOptions(); track object.name) {\r\n <option [value]=\"object.name\">{{ object.label || object.name }}</option>\r\n }\r\n </select>\r\n } @else {\r\n <input\r\n class=\"fb-input\"\r\n [value]=\"record().object || ''\"\r\n placeholder=\"Nome dell\u2019entita\u2019\"\r\n (input)=\"setObject($any($event.target).value)\"\r\n />\r\n }\r\n </div>\r\n\r\n @if (needsFilters()) {\r\n <fb-record-filter-editor\r\n [holder]=\"$any(record())\"\r\n [object]=\"record().object\"\r\n [title]=\"isDelete() ? 'Quali record cancellare' : 'Quali record aggiornare'\"\r\n usage=\"filterable\"\r\n [supportsLogic]=\"supportsFilterLogic()\"\r\n [emptyWarning]=\"emptyFilterWarning()\"\r\n [emptyWarningSeverity]=\"emptyFilterSeverity()\"\r\n (changed)=\"onFiltersChanged($event)\"\r\n />\r\n\r\n @if (showsBulkConfirm()) {\r\n <label class=\"fb-check fb-confirm\">\r\n <input\r\n type=\"checkbox\"\r\n [checked]=\"bulkUpdateConfirmed()\"\r\n (change)=\"confirmBulkUpdate($any($event.target).checked)\"\r\n />\r\n Confermo di voler aggiornare <strong>tutti</strong> i record di \u00AB{{ record().object || 'questo oggetto' }}\u00BB\r\n </label>\r\n }\r\n }\r\n\r\n @if (!isDelete()) {\r\n <fb-field-assignment-editor\r\n [holder]=\"$any(record())\"\r\n [object]=\"record().object\"\r\n [title]=\"isCreate() ? 'Valori del nuovo record' : 'Valori da scrivere'\"\r\n (changed)=\"onAssignmentsChanged($event)\"\r\n />\r\n }\r\n}\r\n\r\n@if (isCreate()) {\r\n <fieldset class=\"fb-section\">\r\n <legend class=\"fb-section__title\">Upsert</legend>\r\n <label class=\"fb-check\">\r\n <input\r\n type=\"checkbox\"\r\n [checked]=\"record().doesUpsert === true\"\r\n (change)=\"setDoesUpsert($any($event.target).checked)\"\r\n />\r\n Aggiorna il record se esiste gi\u00E0\r\n </label>\r\n\r\n @if (record().doesUpsert) {\r\n @if (hasUpsertConflict()) {\r\n <p class=\"fb-callout fb-callout--error\">\r\n Sono indicati insieme il campo di id esterno e quello standard: va scelto uno solo\r\n (UPSERT_CONFIGURATION_INVALID).\r\n </p>\r\n }\r\n <label class=\"fb-check\">\r\n <input\r\n type=\"radio\"\r\n name=\"upsert-mode\"\r\n [checked]=\"upsertMode() === 'external'\"\r\n (change)=\"setUpsertMode('external')\"\r\n />\r\n Riconosci il record da un id esterno\r\n </label>\r\n <label class=\"fb-check\">\r\n <input\r\n type=\"radio\"\r\n name=\"upsert-mode\"\r\n [checked]=\"upsertMode() === 'standard'\"\r\n (change)=\"setUpsertMode('standard')\"\r\n />\r\n Riconosci il record dall\u2019id standard\r\n </label>\r\n\r\n @if (upsertMode() === 'external') {\r\n <div class=\"fb-field\">\r\n <label class=\"fb-field__label fb-field__label--required\">Campo id esterno</label>\r\n <input\r\n class=\"fb-input\"\r\n [value]=\"record().upsertExternalIdField || ''\"\r\n (input)=\"setUpsertExternalField($any($event.target).value)\"\r\n />\r\n </div>\r\n } @else {\r\n <div class=\"fb-field\">\r\n <label class=\"fb-field__label fb-field__label--required\">Campo id standard</label>\r\n <input\r\n class=\"fb-input\"\r\n [value]=\"record().upsertStandardIdField || ''\"\r\n (input)=\"setUpsertStandardField($any($event.target).value)\"\r\n />\r\n </div>\r\n }\r\n }\r\n </fieldset>\r\n\r\n <fieldset class=\"fb-section\">\r\n <legend class=\"fb-section__title\">Identificativo creato</legend>\r\n <label class=\"fb-check\">\r\n <input\r\n type=\"checkbox\"\r\n [checked]=\"record().storeOutputAutomatically === true\"\r\n (change)=\"setStoreOutputAutomatically($any($event.target).checked)\"\r\n />\r\n Output automatico\r\n </label>\r\n @if (record().storeOutputAutomatically) {\r\n <p class=\"fb-field__hint\">\r\n L\u2019identificativo creato si referenzia col nome dell\u2019elemento: <code>{{ name() }}</code>.\r\n </p>\r\n } @else {\r\n <div class=\"fb-field\">\r\n <label class=\"fb-field__label\">Assegna l\u2019identificativo a</label>\r\n <fb-reference-picker\r\n [value]=\"record().assignRecordIdToReference\"\r\n [writableOnly]=\"true\"\r\n placeholder=\"Scegli una variabile\"\r\n (valueChange)=\"setAssignRecordId($event)\"\r\n />\r\n </div>\r\n }\r\n </fieldset>\r\n}\r\n\r\n<fb-connector-editor\r\n [nodeName]=\"name()\"\r\n [node]=\"node()\"\r\n [outlets]=\"outlets()\"\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: FieldAssignmentEditorComponent, selector: "fb-field-assignment-editor", inputs: ["holder", "object", "title", "disabledReason"], outputs: ["changed"] }, { kind: "component", type: RecordFilterEditorComponent, selector: "fb-record-filter-editor", inputs: ["holder", "object", "title", "usage", "supportsLogic", "supportsFormula", "emptyWarning", "emptyWarningSeverity"], outputs: ["changed"] }, { kind: "component", type: ReferencePickerComponent, selector: "fb-reference-picker", inputs: ["value", "label", "placeholder", "disabled", "dataType", "isCollection", "objectType", "writableOnly", "elementsOnly"], outputs: ["valueChange"] }, { kind: "directive", type: SelectValueDirective, selector: "select[fbValue]", inputs: ["fbValue"] }], changeDetection: i0.ChangeDetectionStrategy.OnPush });
5042
5784
  }
5043
5785
  i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.27", ngImport: i0, type: RecordWriteInspectorComponent, decorators: [{
5044
5786
  type: Component,
@@ -5528,7 +6270,7 @@ class SubflowInspectorComponent extends NodeInspectorBase {
5528
6270
  });
5529
6271
  }
5530
6272
  static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.3.27", ngImport: i0, type: SubflowInspectorComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
5531
- static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "20.3.27", type: SubflowInspectorComponent, isStandalone: true, selector: "fb-subflow-inspector", usesInheritance: true, ngImport: i0, template: "<div class=\"fb-field\">\r\n <label class=\"fb-field__label fb-field__label--required\">Flow da invocare</label>\r\n @if (hasCandidates()) {\r\n <select\r\n class=\"fb-select\"\r\n [class.fb-select--invalid]=\"isRecursive()\"\r\n [fbValue]=\"subflow().flowName || ''\"\r\n (change)=\"setFlowName($any($event.target).value)\"\r\n >\r\n <option value=\"\">\u2014 scegli \u2014</option>\r\n @for (candidate of candidateOptions(); track candidate.flowName) {\r\n <option [value]=\"candidate.flowName\">{{ candidate.label || candidate.flowName }}</option>\r\n }\r\n </select>\r\n <p class=\"fb-field__hint\">Solo i flow con una versione attiva possono essere invocati.</p>\r\n } @else {\r\n <input\r\n class=\"fb-input\"\r\n [value]=\"subflow().flowName || ''\"\r\n placeholder=\"API name del flow\"\r\n (input)=\"setFlowName($any($event.target).value)\"\r\n />\r\n <p class=\"fb-field__hint\">\r\n Nessun flow attivo disponibile come subflow: attivane uno, oppure scrivi il nome a mano.\r\n </p>\r\n }\r\n @if (isRecursive()) {\r\n <p class=\"fb-field__error\">Un flow non puo\u2019 invocare se stesso (SUBFLOW_RECURSIVE).</p>\r\n }\r\n</div>\r\n\r\n@if (couldNotLoadTarget()) {\r\n <p class=\"fb-callout fb-callout--warn\">\r\n Non e\u2019 stato possibile leggere la definizione del flow invocato: i nomi di input e output non vengono\r\n proposti, ma puoi scriverli a mano.\r\n </p>\r\n}\r\n\r\n<p class=\"fb-callout\">\r\n Un subflow che si sospende \u2014 cioe\u2019 che contiene screen o Wait \u2014 non e\u2019 supportato dal motore.\r\n</p>\r\n\r\n<fieldset class=\"fb-section\">\r\n <legend class=\"fb-section__title\">Valori passati al subflow</legend>\r\n @if (inputVariables().length) {\r\n <p class=\"fb-section__note\">\r\n Sono le variabili di input del flow invocato: {{ inputVariables().length }} disponibili.\r\n </p>\r\n }\r\n\r\n <div class=\"fb-list\">\r\n @for (assignment of inputAssignments(); track $index) {\r\n <div class=\"fb-list__item\">\r\n <div class=\"fb-list__header\">\r\n @if (inputVariables().length) {\r\n <select\r\n class=\"fb-select\"\r\n [fbValue]=\"assignment.name || ''\"\r\n aria-label=\"Variabile del subflow\"\r\n (change)=\"setInputName($index, $any($event.target).value)\"\r\n >\r\n <option value=\"\">\u2014 scegli \u2014</option>\r\n @for (variable of inputVariables(); track variable.name) {\r\n <option [value]=\"variable.name\">{{ variable.name }}</option>\r\n }\r\n </select>\r\n } @else {\r\n <input\r\n class=\"fb-input\"\r\n [value]=\"assignment.name || ''\"\r\n placeholder=\"Nome della variabile di input\"\r\n aria-label=\"Variabile del subflow\"\r\n (input)=\"setInputName($index, $any($event.target).value)\"\r\n />\r\n }\r\n <button\r\n type=\"button\"\r\n class=\"fb-btn fb-btn--ghost fb-btn--icon\"\r\n aria-label=\"Rimuovi\"\r\n (click)=\"removeInput($index)\"\r\n >\r\n \u00D7\r\n </button>\r\n </div>\r\n <fb-value-editor\r\n [value]=\"assignment.value\"\r\n [dataType]=\"variableOf(assignment.name)?.dataType\"\r\n [objectType]=\"variableOf(assignment.name)?.objectType\"\r\n [isCollection]=\"variableOf(assignment.name)?.isCollection\"\r\n label=\"Valore\"\r\n (valueChange)=\"setInputValue($index, $event)\"\r\n />\r\n </div>\r\n } @empty {\r\n <p class=\"fb-empty\">Nessun valore passato.</p>\r\n }\r\n </div>\r\n <button type=\"button\" class=\"fb-btn\" (click)=\"addInput()\">Aggiungi valore</button>\r\n</fieldset>\r\n\r\n<fieldset class=\"fb-section\">\r\n <legend class=\"fb-section__title\">Valori restituiti</legend>\r\n <label class=\"fb-check\">\r\n <input\r\n type=\"checkbox\"\r\n [checked]=\"storeOutputAutomatically()\"\r\n (change)=\"setStoreOutputAutomatically($any($event.target).checked)\"\r\n />\r\n Output automatico\r\n </label>\r\n @if (storeOutputAutomatically()) {\r\n <p class=\"fb-field__hint\">\r\n Le variabili di output del subflow si referenziano col nome dell\u2019elemento:\r\n <code>{{ name() }}.NomeVariabile</code>.\r\n </p>\r\n } @else {\r\n <div class=\"fb-list\">\r\n @for (assignment of outputAssignments(); track $index) {\r\n <div class=\"fb-list__item\">\r\n <div class=\"fb-list__header\">\r\n @if (outputVariables().length) {\r\n <select\r\n class=\"fb-select\"\r\n [fbValue]=\"assignment.name || ''\"\r\n aria-label=\"Variabile di output del subflow\"\r\n (change)=\"setOutputName($index, $any($event.target).value)\"\r\n >\r\n <option value=\"\">\u2014 scegli \u2014</option>\r\n @for (variable of outputVariables(); track variable.name) {\r\n <option [value]=\"variable.name\">{{ variable.name }}</option>\r\n }\r\n </select>\r\n } @else {\r\n <input\r\n class=\"fb-input\"\r\n [value]=\"assignment.name || ''\"\r\n placeholder=\"Nome della variabile di output\"\r\n aria-label=\"Variabile di output del subflow\"\r\n (input)=\"setOutputName($index, $any($event.target).value)\"\r\n />\r\n }\r\n <button\r\n type=\"button\"\r\n class=\"fb-btn fb-btn--ghost fb-btn--icon\"\r\n aria-label=\"Rimuovi\"\r\n (click)=\"removeOutput($index)\"\r\n >\r\n \u00D7\r\n </button>\r\n </div>\r\n <div class=\"fb-field\">\r\n <label class=\"fb-field__label\">Assegna a</label>\r\n <fb-reference-picker\r\n [value]=\"assignment.assignToReference\"\r\n [writableOnly]=\"true\"\r\n [dataType]=\"variableOf(assignment.name)?.dataType\"\r\n [isCollection]=\"variableOf(assignment.name)?.isCollection\"\r\n placeholder=\"Scegli una variabile\"\r\n (valueChange)=\"setOutputTarget($index, $event)\"\r\n />\r\n </div>\r\n </div>\r\n } @empty {\r\n <p class=\"fb-empty\">Nessun valore raccolto.</p>\r\n }\r\n </div>\r\n <button type=\"button\" class=\"fb-btn\" (click)=\"addOutput()\">Aggiungi valore</button>\r\n }\r\n</fieldset>\r\n\r\n<fb-connector-editor\r\n [nodeName]=\"name()\"\r\n [node]=\"node()\"\r\n [outlets]=\"outlets()\"\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: ReferencePickerComponent, selector: "fb-reference-picker", inputs: ["value", "label", "placeholder", "disabled", "dataType", "isCollection", "objectType", "writableOnly", "allowStageTargets", "elementsOnly"], outputs: ["valueChange"] }, { kind: "component", type: ValueEditorComponent, selector: "fb-value-editor", inputs: ["value", "label", "dataType", "objectType", "isCollection", "disabled", "allowFormula"], outputs: ["valueChange"] }, { kind: "directive", type: SelectValueDirective, selector: "select[fbValue]", inputs: ["fbValue"] }], changeDetection: i0.ChangeDetectionStrategy.OnPush });
6273
+ static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "20.3.27", type: SubflowInspectorComponent, isStandalone: true, selector: "fb-subflow-inspector", usesInheritance: true, ngImport: i0, template: "<div class=\"fb-field\">\r\n <label class=\"fb-field__label fb-field__label--required\">Flow da invocare</label>\r\n @if (hasCandidates()) {\r\n <select\r\n class=\"fb-select\"\r\n [class.fb-select--invalid]=\"isRecursive()\"\r\n [fbValue]=\"subflow().flowName || ''\"\r\n (change)=\"setFlowName($any($event.target).value)\"\r\n >\r\n <option value=\"\">\u2014 scegli \u2014</option>\r\n @for (candidate of candidateOptions(); track candidate.flowName) {\r\n <option [value]=\"candidate.flowName\">{{ candidate.label || candidate.flowName }}</option>\r\n }\r\n </select>\r\n <p class=\"fb-field__hint\">Solo i flow con una versione attiva possono essere invocati.</p>\r\n } @else {\r\n <input\r\n class=\"fb-input\"\r\n [value]=\"subflow().flowName || ''\"\r\n placeholder=\"API name del flow\"\r\n (input)=\"setFlowName($any($event.target).value)\"\r\n />\r\n <p class=\"fb-field__hint\">\r\n Nessun flow attivo disponibile come subflow: attivane uno, oppure scrivi il nome a mano.\r\n </p>\r\n }\r\n @if (isRecursive()) {\r\n <p class=\"fb-field__error\">Un flow non puo\u2019 invocare se stesso (SUBFLOW_RECURSIVE).</p>\r\n }\r\n</div>\r\n\r\n@if (couldNotLoadTarget()) {\r\n <p class=\"fb-callout fb-callout--warn\">\r\n Non e\u2019 stato possibile leggere la definizione del flow invocato: i nomi di input e output non vengono\r\n proposti, ma puoi scriverli a mano.\r\n </p>\r\n}\r\n\r\n<p class=\"fb-callout\">\r\n Un subflow che si sospende \u2014 cioe\u2019 che contiene screen o Wait \u2014 non e\u2019 supportato dal motore.\r\n</p>\r\n\r\n<fieldset class=\"fb-section\">\r\n <legend class=\"fb-section__title\">Valori passati al subflow</legend>\r\n @if (inputVariables().length) {\r\n <p class=\"fb-section__note\">\r\n Sono le variabili di input del flow invocato: {{ inputVariables().length }} disponibili.\r\n </p>\r\n }\r\n\r\n <div class=\"fb-list\">\r\n @for (assignment of inputAssignments(); track $index) {\r\n <div class=\"fb-list__item\">\r\n <div class=\"fb-list__header\">\r\n @if (inputVariables().length) {\r\n <select\r\n class=\"fb-select\"\r\n [fbValue]=\"assignment.name || ''\"\r\n aria-label=\"Variabile del subflow\"\r\n (change)=\"setInputName($index, $any($event.target).value)\"\r\n >\r\n <option value=\"\">\u2014 scegli \u2014</option>\r\n @for (variable of inputVariables(); track variable.name) {\r\n <option [value]=\"variable.name\">{{ variable.name }}</option>\r\n }\r\n </select>\r\n } @else {\r\n <input\r\n class=\"fb-input\"\r\n [value]=\"assignment.name || ''\"\r\n placeholder=\"Nome della variabile di input\"\r\n aria-label=\"Variabile del subflow\"\r\n (input)=\"setInputName($index, $any($event.target).value)\"\r\n />\r\n }\r\n <button\r\n type=\"button\"\r\n class=\"fb-btn fb-btn--ghost fb-btn--icon\"\r\n aria-label=\"Rimuovi\"\r\n (click)=\"removeInput($index)\"\r\n >\r\n \u00D7\r\n </button>\r\n </div>\r\n <fb-value-editor\r\n [value]=\"assignment.value\"\r\n [dataType]=\"variableOf(assignment.name)?.dataType\"\r\n [objectType]=\"variableOf(assignment.name)?.objectType\"\r\n [isCollection]=\"variableOf(assignment.name)?.isCollection\"\r\n label=\"Valore\"\r\n (valueChange)=\"setInputValue($index, $event)\"\r\n />\r\n </div>\r\n } @empty {\r\n <p class=\"fb-empty\">Nessun valore passato.</p>\r\n }\r\n </div>\r\n <button type=\"button\" class=\"fb-btn\" (click)=\"addInput()\">Aggiungi valore</button>\r\n</fieldset>\r\n\r\n<fieldset class=\"fb-section\">\r\n <legend class=\"fb-section__title\">Valori restituiti</legend>\r\n <label class=\"fb-check\">\r\n <input\r\n type=\"checkbox\"\r\n [checked]=\"storeOutputAutomatically()\"\r\n (change)=\"setStoreOutputAutomatically($any($event.target).checked)\"\r\n />\r\n Output automatico\r\n </label>\r\n @if (storeOutputAutomatically()) {\r\n <p class=\"fb-field__hint\">\r\n Le variabili di output del subflow si referenziano col nome dell\u2019elemento:\r\n <code>{{ name() }}.NomeVariabile</code>.\r\n </p>\r\n } @else {\r\n <div class=\"fb-list\">\r\n @for (assignment of outputAssignments(); track $index) {\r\n <div class=\"fb-list__item\">\r\n <div class=\"fb-list__header\">\r\n @if (outputVariables().length) {\r\n <select\r\n class=\"fb-select\"\r\n [fbValue]=\"assignment.name || ''\"\r\n aria-label=\"Variabile di output del subflow\"\r\n (change)=\"setOutputName($index, $any($event.target).value)\"\r\n >\r\n <option value=\"\">\u2014 scegli \u2014</option>\r\n @for (variable of outputVariables(); track variable.name) {\r\n <option [value]=\"variable.name\">{{ variable.name }}</option>\r\n }\r\n </select>\r\n } @else {\r\n <input\r\n class=\"fb-input\"\r\n [value]=\"assignment.name || ''\"\r\n placeholder=\"Nome della variabile di output\"\r\n aria-label=\"Variabile di output del subflow\"\r\n (input)=\"setOutputName($index, $any($event.target).value)\"\r\n />\r\n }\r\n <button\r\n type=\"button\"\r\n class=\"fb-btn fb-btn--ghost fb-btn--icon\"\r\n aria-label=\"Rimuovi\"\r\n (click)=\"removeOutput($index)\"\r\n >\r\n \u00D7\r\n </button>\r\n </div>\r\n <div class=\"fb-field\">\r\n <label class=\"fb-field__label\">Assegna a</label>\r\n <fb-reference-picker\r\n [value]=\"assignment.assignToReference\"\r\n [writableOnly]=\"true\"\r\n [dataType]=\"variableOf(assignment.name)?.dataType\"\r\n [isCollection]=\"variableOf(assignment.name)?.isCollection\"\r\n placeholder=\"Scegli una variabile\"\r\n (valueChange)=\"setOutputTarget($index, $event)\"\r\n />\r\n </div>\r\n </div>\r\n } @empty {\r\n <p class=\"fb-empty\">Nessun valore raccolto.</p>\r\n }\r\n </div>\r\n <button type=\"button\" class=\"fb-btn\" (click)=\"addOutput()\">Aggiungi valore</button>\r\n }\r\n</fieldset>\r\n\r\n<fb-connector-editor\r\n [nodeName]=\"name()\"\r\n [node]=\"node()\"\r\n [outlets]=\"outlets()\"\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: ReferencePickerComponent, selector: "fb-reference-picker", inputs: ["value", "label", "placeholder", "disabled", "dataType", "isCollection", "objectType", "writableOnly", "elementsOnly"], outputs: ["valueChange"] }, { kind: "component", type: ValueEditorComponent, selector: "fb-value-editor", inputs: ["value", "label", "dataType", "objectType", "isCollection", "disabled", "allowFormula"], outputs: ["valueChange"] }, { kind: "directive", type: SelectValueDirective, selector: "select[fbValue]", inputs: ["fbValue"] }], changeDetection: i0.ChangeDetectionStrategy.OnPush });
5532
6274
  }
5533
6275
  i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.27", ngImport: i0, type: SubflowInspectorComponent, decorators: [{
5534
6276
  type: Component,
@@ -5693,7 +6435,7 @@ class TransformInspectorComponent extends NodeInspectorBase {
5693
6435
  return action.transformType === 'Sum' || action.transformType === 'Count';
5694
6436
  }
5695
6437
  static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.3.27", ngImport: i0, type: TransformInspectorComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
5696
- static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "20.3.27", type: TransformInspectorComponent, isStandalone: true, selector: "fb-transform-inspector", usesInheritance: true, ngImport: i0, template: "<div class=\"fb-field\">\r\n <label class=\"fb-field__label fb-field__label--required\">Tipo del risultato</label>\r\n <select class=\"fb-select\" [fbValue]=\"transform().dataType || ''\" (change)=\"setDataType($any($event.target).value)\">\r\n <option value=\"\">\u2014 scegli \u2014</option>\r\n @for (type of dataTypes(); track type.value) {\r\n <option [value]=\"type.value\">{{ type.label }}</option>\r\n }\r\n </select>\r\n @if (!transform().dataType) {\r\n <p class=\"fb-field__error\">Obbligatorio (TRANSFORM_DATA_TYPE_MISSING).</p>\r\n }\r\n</div>\r\n\r\n@if (requiresObjectType()) {\r\n <div class=\"fb-field\">\r\n <label class=\"fb-field__label fb-field__label--required\">Tipo dell\u2019oggetto</label>\r\n <select\r\n class=\"fb-select\"\r\n [fbValue]=\"transform().objectType || ''\"\r\n (change)=\"setObjectType($any($event.target).value)\"\r\n >\r\n <option value=\"\">\u2014 scegli \u2014</option>\r\n @if (transform().dataType === 'Enum') {\r\n @for (entry of enumOptions(); track entry.name) {\r\n <option [value]=\"entry.name\">{{ entry.label || entry.name }}</option>\r\n }\r\n } @else {\r\n @for (object of objectOptions(); track object.name) {\r\n <option [value]=\"object.name\">{{ object.label || object.name }}</option>\r\n }\r\n }\r\n </select>\r\n </div>\r\n}\r\n\r\n<label class=\"fb-check\">\r\n <input\r\n type=\"checkbox\"\r\n [checked]=\"transform().isCollection === true\"\r\n (change)=\"setIsCollection($any($event.target).checked)\"\r\n />\r\n Il risultato e\u2019 una collection\r\n</label>\r\n\r\n<p class=\"fb-callout\">\r\n Il risultato e\u2019 l\u2019<strong>output automatico</strong> dell\u2019elemento: si referenzia con\r\n <code>{{ name() }}</code>.\r\n</p>\r\n\r\n<fieldset class=\"fb-section\">\r\n <legend class=\"fb-section__title\">Trasformazioni</legend>\r\n\r\n <div class=\"fb-list\">\r\n @for (action of actions(); 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\"\r\n (click)=\"removeAction($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\">Operazione</label>\r\n <select\r\n class=\"fb-select\"\r\n [fbValue]=\"action.transformType || 'Map'\"\r\n (change)=\"setActionType($index, $any($event.target).value)\"\r\n >\r\n @for (type of transformTypes(); track type.value) {\r\n <option [value]=\"type.value\">{{ type.label }}</option>\r\n }\r\n </select>\r\n </div>\r\n\r\n <div class=\"fb-field\">\r\n <label class=\"fb-field__label fb-field__label--required\">Campo di destinazione</label>\r\n <input\r\n class=\"fb-input\"\r\n [value]=\"action.outputFieldApiName || ''\"\r\n placeholder=\"Totale\"\r\n (input)=\"setOutputField($index, $any($event.target).value)\"\r\n />\r\n </div>\r\n\r\n @if (isMap(action)) {\r\n <div class=\"fb-field\">\r\n <label class=\"fb-field__label\">Valore</label>\r\n <fb-value-editor\r\n [value]=\"action.value\"\r\n label=\"Valore\"\r\n (valueChange)=\"setMapValue($index, $event)\"\r\n />\r\n </div>\r\n }\r\n\r\n @if (needsAggregationValues(action)) {\r\n <div class=\"fb-field\">\r\n <label class=\"fb-field__label fb-field__label--required\">Collection su cui aggregare</label>\r\n <fb-reference-picker\r\n [value]=\"aggregationCollection(action)\"\r\n [isCollection]=\"true\"\r\n placeholder=\"Scegli una collection\"\r\n (valueChange)=\"setAggregationCollection($index, $event)\"\r\n />\r\n </div>\r\n }\r\n\r\n @if (needsAggregationField(action)) {\r\n <div class=\"fb-field\">\r\n <label class=\"fb-field__label fb-field__label--required\">Campo da sommare</label>\r\n <input\r\n class=\"fb-input\"\r\n [value]=\"aggregationField(action)\"\r\n placeholder=\"Importo\"\r\n (input)=\"setAggregationField($index, $any($event.target).value)\"\r\n />\r\n </div>\r\n }\r\n </div>\r\n } @empty {\r\n <p class=\"fb-empty\">Nessuna trasformazione: l\u2019elemento non produce nulla (TRANSFORM_WITHOUT_VALUES).</p>\r\n }\r\n </div>\r\n\r\n <button type=\"button\" class=\"fb-btn\" (click)=\"addAction()\">Aggiungi trasformazione</button>\r\n</fieldset>\r\n\r\n<fb-connector-editor\r\n [nodeName]=\"name()\"\r\n [node]=\"node()\"\r\n [outlets]=\"outlets()\"\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: ReferencePickerComponent, selector: "fb-reference-picker", inputs: ["value", "label", "placeholder", "disabled", "dataType", "isCollection", "objectType", "writableOnly", "allowStageTargets", "elementsOnly"], outputs: ["valueChange"] }, { kind: "component", type: ValueEditorComponent, selector: "fb-value-editor", inputs: ["value", "label", "dataType", "objectType", "isCollection", "disabled", "allowFormula"], outputs: ["valueChange"] }, { kind: "directive", type: SelectValueDirective, selector: "select[fbValue]", inputs: ["fbValue"] }], changeDetection: i0.ChangeDetectionStrategy.OnPush });
6438
+ static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "20.3.27", type: TransformInspectorComponent, isStandalone: true, selector: "fb-transform-inspector", usesInheritance: true, ngImport: i0, template: "<div class=\"fb-field\">\r\n <label class=\"fb-field__label fb-field__label--required\">Tipo del risultato</label>\r\n <select class=\"fb-select\" [fbValue]=\"transform().dataType || ''\" (change)=\"setDataType($any($event.target).value)\">\r\n <option value=\"\">\u2014 scegli \u2014</option>\r\n @for (type of dataTypes(); track type.value) {\r\n <option [value]=\"type.value\">{{ type.label }}</option>\r\n }\r\n </select>\r\n @if (!transform().dataType) {\r\n <p class=\"fb-field__error\">Obbligatorio (TRANSFORM_DATA_TYPE_MISSING).</p>\r\n }\r\n</div>\r\n\r\n@if (requiresObjectType()) {\r\n <div class=\"fb-field\">\r\n <label class=\"fb-field__label fb-field__label--required\">Tipo dell\u2019oggetto</label>\r\n <select\r\n class=\"fb-select\"\r\n [fbValue]=\"transform().objectType || ''\"\r\n (change)=\"setObjectType($any($event.target).value)\"\r\n >\r\n <option value=\"\">\u2014 scegli \u2014</option>\r\n @if (transform().dataType === 'Enum') {\r\n @for (entry of enumOptions(); track entry.name) {\r\n <option [value]=\"entry.name\">{{ entry.label || entry.name }}</option>\r\n }\r\n } @else {\r\n @for (object of objectOptions(); track object.name) {\r\n <option [value]=\"object.name\">{{ object.label || object.name }}</option>\r\n }\r\n }\r\n </select>\r\n </div>\r\n}\r\n\r\n<label class=\"fb-check\">\r\n <input\r\n type=\"checkbox\"\r\n [checked]=\"transform().isCollection === true\"\r\n (change)=\"setIsCollection($any($event.target).checked)\"\r\n />\r\n Il risultato e\u2019 una collection\r\n</label>\r\n\r\n<p class=\"fb-callout\">\r\n Il risultato e\u2019 l\u2019<strong>output automatico</strong> dell\u2019elemento: si referenzia con\r\n <code>{{ name() }}</code>.\r\n</p>\r\n\r\n<fieldset class=\"fb-section\">\r\n <legend class=\"fb-section__title\">Trasformazioni</legend>\r\n\r\n <div class=\"fb-list\">\r\n @for (action of actions(); 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\"\r\n (click)=\"removeAction($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\">Operazione</label>\r\n <select\r\n class=\"fb-select\"\r\n [fbValue]=\"action.transformType || 'Map'\"\r\n (change)=\"setActionType($index, $any($event.target).value)\"\r\n >\r\n @for (type of transformTypes(); track type.value) {\r\n <option [value]=\"type.value\">{{ type.label }}</option>\r\n }\r\n </select>\r\n </div>\r\n\r\n <div class=\"fb-field\">\r\n <label class=\"fb-field__label fb-field__label--required\">Campo di destinazione</label>\r\n <input\r\n class=\"fb-input\"\r\n [value]=\"action.outputFieldApiName || ''\"\r\n placeholder=\"Totale\"\r\n (input)=\"setOutputField($index, $any($event.target).value)\"\r\n />\r\n </div>\r\n\r\n @if (isMap(action)) {\r\n <div class=\"fb-field\">\r\n <label class=\"fb-field__label\">Valore</label>\r\n <fb-value-editor\r\n [value]=\"action.value\"\r\n label=\"Valore\"\r\n (valueChange)=\"setMapValue($index, $event)\"\r\n />\r\n </div>\r\n }\r\n\r\n @if (needsAggregationValues(action)) {\r\n <div class=\"fb-field\">\r\n <label class=\"fb-field__label fb-field__label--required\">Collection su cui aggregare</label>\r\n <fb-reference-picker\r\n [value]=\"aggregationCollection(action)\"\r\n [isCollection]=\"true\"\r\n placeholder=\"Scegli una collection\"\r\n (valueChange)=\"setAggregationCollection($index, $event)\"\r\n />\r\n </div>\r\n }\r\n\r\n @if (needsAggregationField(action)) {\r\n <div class=\"fb-field\">\r\n <label class=\"fb-field__label fb-field__label--required\">Campo da sommare</label>\r\n <input\r\n class=\"fb-input\"\r\n [value]=\"aggregationField(action)\"\r\n placeholder=\"Importo\"\r\n (input)=\"setAggregationField($index, $any($event.target).value)\"\r\n />\r\n </div>\r\n }\r\n </div>\r\n } @empty {\r\n <p class=\"fb-empty\">Nessuna trasformazione: l\u2019elemento non produce nulla (TRANSFORM_WITHOUT_VALUES).</p>\r\n }\r\n </div>\r\n\r\n <button type=\"button\" class=\"fb-btn\" (click)=\"addAction()\">Aggiungi trasformazione</button>\r\n</fieldset>\r\n\r\n<fb-connector-editor\r\n [nodeName]=\"name()\"\r\n [node]=\"node()\"\r\n [outlets]=\"outlets()\"\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: ReferencePickerComponent, selector: "fb-reference-picker", inputs: ["value", "label", "placeholder", "disabled", "dataType", "isCollection", "objectType", "writableOnly", "elementsOnly"], outputs: ["valueChange"] }, { kind: "component", type: ValueEditorComponent, selector: "fb-value-editor", inputs: ["value", "label", "dataType", "objectType", "isCollection", "disabled", "allowFormula"], outputs: ["valueChange"] }, { kind: "directive", type: SelectValueDirective, selector: "select[fbValue]", inputs: ["fbValue"] }], changeDetection: i0.ChangeDetectionStrategy.OnPush });
5697
6439
  }
5698
6440
  i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.27", ngImport: i0, type: TransformInspectorComponent, decorators: [{
5699
6441
  type: Component,
@@ -5808,7 +6550,7 @@ class WaitInspectorComponent extends NodeInspectorBase {
5808
6550
  this.setField('defaultConnectorLabel', label);
5809
6551
  }
5810
6552
  static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.3.27", ngImport: i0, type: WaitInspectorComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
5811
- static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "20.3.27", type: WaitInspectorComponent, isStandalone: true, selector: "fb-wait-inspector", usesInheritance: true, ngImport: i0, template: "<div class=\"fb-list\">\r\n @for (event of events(); 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=\"Sposta su\"\r\n [disabled]=\"$first\"\r\n (click)=\"moveEvent($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)=\"moveEvent($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 l\u2019evento\"\r\n (click)=\"removeEvent($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 fb-field__label--required\">Etichetta del ramo</label>\r\n <input\r\n class=\"fb-input\"\r\n [value]=\"event.label || ''\"\r\n placeholder=\"Alla scadenza\"\r\n (input)=\"setEventLabel($index, $any($event.target).value)\"\r\n />\r\n </div>\r\n\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]=\"event.name || ''\"\r\n (input)=\"setEventName($index, $any($event.target).value)\"\r\n />\r\n </div>\r\n\r\n <div class=\"fb-field\">\r\n <label class=\"fb-field__label fb-field__label--required\">Tipo di evento</label>\r\n <select\r\n class=\"fb-select\"\r\n [fbValue]=\"event.eventType || ''\"\r\n (change)=\"setEventType($index, $any($event.target).value)\"\r\n >\r\n <option value=\"\">\u2014 scegli \u2014</option>\r\n @for (type of eventTypes(); track type.value) {\r\n <option [value]=\"type.value\">{{ type.label }}</option>\r\n }\r\n @for (entry of catalogEventOptions(); track entry.name) {\r\n <option [value]=\"entry.name\">{{ entry.label || entry.name }}</option>\r\n }\r\n </select>\r\n @if (!event.eventType) {\r\n <p class=\"fb-field__error\">Senza tipo l\u2019evento non e\u2019 valido (WAIT_EVENT_TYPE_MISSING).</p>\r\n }\r\n </div>\r\n\r\n <fb-parameter-editor\r\n [holder]=\"event\"\r\n inputTitle=\"Parametri dell\u2019evento\"\r\n [showOutputs]=\"true\"\r\n outputTitle=\"Valori prodotti dall\u2019evento\"\r\n (changed)=\"onEventParametersChanged($index, $event)\"\r\n />\r\n\r\n <fb-condition-editor\r\n [holder]=\"event\"\r\n title=\"Condizioni dell\u2019evento\"\r\n (changed)=\"onEventConditionsChanged($index, $event)\"\r\n />\r\n </div>\r\n } @empty {\r\n <p class=\"fb-empty\">\r\n Almeno un evento e\u2019 obbligatorio: un Wait senza eventi non e\u2019 eseguibile (WAIT_WITHOUT_EVENTS).\r\n </p>\r\n }\r\n</div>\r\n\r\n<button type=\"button\" class=\"fb-btn\" (click)=\"addEvent()\">Aggiungi evento</button>\r\n\r\n<fieldset class=\"fb-section\">\r\n <legend class=\"fb-section__title\">Se nessun evento si verifica</legend>\r\n <div class=\"fb-field\">\r\n <label class=\"fb-field__label\">Etichetta del ramo di default</label>\r\n <input\r\n class=\"fb-input\"\r\n [value]=\"wait().defaultConnectorLabel || ''\"\r\n (input)=\"setDefaultLabel($any($event.target).value)\"\r\n />\r\n <p class=\"fb-field__hint\">\r\n \u00C8 il ramo percorso quando le condizioni di <strong>tutti</strong> gli eventi sono false.\r\n </p>\r\n </div>\r\n</fieldset>\r\n\r\n<fb-connector-editor\r\n [nodeName]=\"name()\"\r\n [node]=\"node()\"\r\n [outlets]=\"outlets()\"\r\n title=\"Rami\"\r\n (connectorChanged)=\"onConnectorChanged($event)\"\r\n/>\r\n", dependencies: [{ kind: "component", type: ConditionEditorComponent, selector: "fb-condition-editor", inputs: ["holder", "title", "allowFormula", "issuePath"], outputs: ["changed"] }, { kind: "component", type: ConnectorEditorComponent, selector: "fb-connector-editor", inputs: ["nodeName", "node", "outlets", "title"], outputs: ["connectorChanged"] }, { kind: "component", type: ParameterEditorComponent, selector: "fb-parameter-editor", inputs: ["holder", "catalogParameters", "inputTitle", "outputTitle", "showInputs", "showOutputs", "outputsDisabledReason"], outputs: ["changed"] }, { kind: "directive", type: SelectValueDirective, selector: "select[fbValue]", inputs: ["fbValue"] }], changeDetection: i0.ChangeDetectionStrategy.OnPush });
6553
+ static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "20.3.27", type: WaitInspectorComponent, isStandalone: true, selector: "fb-wait-inspector", usesInheritance: true, ngImport: i0, template: "<div class=\"fb-list\">\r\n @for (event of events(); 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=\"Sposta su\"\r\n [disabled]=\"$first\"\r\n (click)=\"moveEvent($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)=\"moveEvent($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 l\u2019evento\"\r\n (click)=\"removeEvent($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 fb-field__label--required\">Etichetta del ramo</label>\r\n <input\r\n class=\"fb-input\"\r\n [value]=\"event.label || ''\"\r\n placeholder=\"Alla scadenza\"\r\n (input)=\"setEventLabel($index, $any($event.target).value)\"\r\n />\r\n </div>\r\n\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]=\"event.name || ''\"\r\n (input)=\"setEventName($index, $any($event.target).value)\"\r\n />\r\n </div>\r\n\r\n <div class=\"fb-field\">\r\n <label class=\"fb-field__label fb-field__label--required\">Tipo di evento</label>\r\n <select\r\n class=\"fb-select\"\r\n [fbValue]=\"event.eventType || ''\"\r\n (change)=\"setEventType($index, $any($event.target).value)\"\r\n >\r\n <option value=\"\">\u2014 scegli \u2014</option>\r\n @for (type of eventTypes(); track type.value) {\r\n <option [value]=\"type.value\">{{ type.label }}</option>\r\n }\r\n @for (entry of catalogEventOptions(); track entry.name) {\r\n <option [value]=\"entry.name\">{{ entry.label || entry.name }}</option>\r\n }\r\n </select>\r\n @if (!event.eventType) {\r\n <p class=\"fb-field__error\">Senza tipo l\u2019evento non e\u2019 valido (WAIT_EVENT_TYPE_MISSING).</p>\r\n }\r\n </div>\r\n\r\n <fb-parameter-editor\r\n [holder]=\"event\"\r\n inputTitle=\"Parametri dell\u2019evento\"\r\n [showOutputs]=\"true\"\r\n outputTitle=\"Valori prodotti dall\u2019evento\"\r\n (changed)=\"onEventParametersChanged($index, $event)\"\r\n />\r\n\r\n <fb-condition-editor\r\n [holder]=\"event\"\r\n title=\"Condizioni dell\u2019evento\"\r\n (changed)=\"onEventConditionsChanged($index, $event)\"\r\n />\r\n </div>\r\n } @empty {\r\n <p class=\"fb-empty\">\r\n Almeno un evento e\u2019 obbligatorio: un Wait senza eventi non e\u2019 eseguibile (WAIT_WITHOUT_EVENTS).\r\n </p>\r\n }\r\n</div>\r\n\r\n<button type=\"button\" class=\"fb-btn\" (click)=\"addEvent()\">Aggiungi evento</button>\r\n\r\n<fieldset class=\"fb-section\">\r\n <legend class=\"fb-section__title\">Se nessun evento si verifica</legend>\r\n <div class=\"fb-field\">\r\n <label class=\"fb-field__label\">Etichetta del ramo di default</label>\r\n <input\r\n class=\"fb-input\"\r\n [value]=\"wait().defaultConnectorLabel || ''\"\r\n (input)=\"setDefaultLabel($any($event.target).value)\"\r\n />\r\n <p class=\"fb-field__hint\">\r\n \u00C8 il ramo percorso quando le condizioni di <strong>tutti</strong> gli eventi sono false.\r\n </p>\r\n </div>\r\n</fieldset>\r\n\r\n<fb-connector-editor\r\n [nodeName]=\"name()\"\r\n [node]=\"node()\"\r\n [outlets]=\"outlets()\"\r\n title=\"Rami\"\r\n (connectorChanged)=\"onConnectorChanged($event)\"\r\n/>\r\n", dependencies: [{ kind: "component", type: ConditionEditorComponent, selector: "fb-condition-editor", inputs: ["holder", "title", "allowFormula", "allowLogic", "issuePath"], outputs: ["changed"] }, { kind: "component", type: ConnectorEditorComponent, selector: "fb-connector-editor", inputs: ["nodeName", "node", "outlets", "title"], outputs: ["connectorChanged"] }, { kind: "component", type: ParameterEditorComponent, selector: "fb-parameter-editor", inputs: ["holder", "catalogParameters", "inputTitle", "outputTitle", "showInputs", "showOutputs", "outputsDisabledReason"], outputs: ["changed"] }, { kind: "directive", type: SelectValueDirective, selector: "select[fbValue]", inputs: ["fbValue"] }], changeDetection: i0.ChangeDetectionStrategy.OnPush });
5812
6554
  }
5813
6555
  i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.27", ngImport: i0, type: WaitInspectorComponent, decorators: [{
5814
6556
  type: Component,
@@ -5824,9 +6566,9 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.27", ngImpo
5824
6566
  * `FlowDocumentStore.renameNode`, e il form dice quanti riferimenti verranno riscritti prima
5825
6567
  * di farlo.
5826
6568
  *
5827
- * Il corpo e' scelto per tipo. I tipi non supportati (`Step`, `Experiment`,
5828
- * `OrchestratedStage`) non sono creabili dalla palette, ma possono arrivare da un documento
5829
- * importato: in quel caso l'inspector li mostra in sola lettura, dicendo perche'.
6569
+ * Il corpo e' scelto per tipo. I tipi non supportati (`Step`, `Experiment`) non sono creabili
6570
+ * dalla palette, ma possono arrivare da un documento importato: in quel caso l'inspector li
6571
+ * mostra in sola lettura, dicendo perche'.
5830
6572
  */
5831
6573
  class ElementInspectorComponent {
5832
6574
  store = inject(FlowDocumentStore);
@@ -5964,7 +6706,7 @@ class ElementInspectorComponent {
5964
6706
  return { x: node?.locationX ?? 0, y: node?.locationY ?? 0 };
5965
6707
  }, ...(ngDevMode ? [{ debugName: "position" }] : []));
5966
6708
  static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.3.27", ngImport: i0, type: ElementInspectorComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
5967
- static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "20.3.27", type: ElementInspectorComponent, isStandalone: true, selector: "fb-element-inspector", inputs: { selectedName: { classPropertyName: "selectedName", publicName: "selectedName", isSignal: true, isRequired: false, transformFunction: null }, showHeader: { classPropertyName: "showHeader", publicName: "showHeader", isSignal: true, isRequired: false, transformFunction: null } }, outputs: { closed: "closed", removeRequested: "removeRequested", duplicateRequested: "duplicateRequested" }, ngImport: i0, template: "@if (!selectedName()) {\n <p class=\"fb-inspector__empty\">\n Seleziona un elemento sul canvas per modificarlo, oppure trascina un elemento dalla palette.\n </p>\n} @else {\n @if (showHeader()) {\n <header class=\"fb-inspector__header\">\n <div>\n <span class=\"fb-inspector__type\">{{ typeLabel() }}</span>\n <h2 class=\"fb-inspector__title\">\n {{ isStart() ? 'Avvio del flow' : node()?.label || selectedName() }}\n </h2>\n </div>\n <button type=\"button\" class=\"fb-btn fb-btn--ghost fb-btn--icon\" aria-label=\"Chiudi\" (click)=\"close()\">\n \u00D7\n </button>\n </header>\n }\n\n <div class=\"fb-inspector__body\">\n @if (issues().length) {\n <ul class=\"fb-inspector__issues\">\n <!-- `$index`: due rilievi con lo stesso codice e lo stesso path sono possibili. -->\n @for (issue of issues(); track $index) {\n <li\n class=\"fb-inspector__issue\"\n [class.fb-inspector__issue--error]=\"issue.severity === 'Error'\"\n [class.fb-inspector__issue--warning]=\"issue.severity === 'Warning'\"\n >\n <span class=\"fb-inspector__issue-code\">{{ issue.code }}</span>\n {{ issue.message }}\n @if (issue.path) {\n <span class=\"fb-inspector__issue-path\">{{ issue.path }}</span>\n }\n </li>\n }\n </ul>\n }\n\n @if (isUnsupported()) {\n <p class=\"fb-callout fb-callout--error\">\n Questo tipo di elemento non e\u2019 supportato dal motore: il flow che lo contiene non parte\n (ELEMENT_NOT_SUPPORTED). Non e\u2019 creabile dalla palette; se e\u2019 arrivato da un documento importato,\n va rimosso.\n </p>\n }\n\n @if (!isStart()) {\n <div class=\"fb-field\">\n <label class=\"fb-field__label\">Etichetta</label>\n <input\n class=\"fb-input\"\n [value]=\"$any(node()?.label) || ''\"\n placeholder=\"Nome mostrato sul canvas\"\n (input)=\"setLabel($any($event.target).value)\"\n />\n </div>\n\n <div class=\"fb-field\">\n <label class=\"fb-field__label\">Nome tecnico</label>\n @if (pendingName() === null) {\n <div class=\"fb-field__row\">\n <input class=\"fb-input fb-input--mono\" [value]=\"selectedName() || ''\" readonly />\n <button type=\"button\" class=\"fb-btn\" (click)=\"startRename()\">Rinomina</button>\n </div>\n <p class=\"fb-field__hint\">\n \u00C8 l\u2019identificatore con cui i riferimenti raggiungono questo elemento.\n @if (referenceCount() > 1) {\n Compare {{ referenceCount() }} volte nel documento.\n }\n </p>\n } @else {\n <input\n class=\"fb-input fb-input--mono\"\n [class.fb-input--invalid]=\"!!nameError()\"\n [value]=\"pendingName() || ''\"\n (input)=\"onPendingNameInput($any($event.target).value)\"\n />\n @if (nameError()) {\n <p class=\"fb-field__error\">{{ nameError() }}</p>\n } @else {\n <p class=\"fb-field__hint fb-field__hint--warn\">\n La rinomina riscrive tutti i riferimenti che puntano a questo elemento\n ({{ referenceCount() }} occorrenze): nessuna primitiva del backend lo fa, lo fa l\u2019editor.\n </p>\n }\n <div class=\"fb-field__row\">\n <button type=\"button\" class=\"fb-btn fb-btn--primary\" [disabled]=\"!canRename()\" (click)=\"applyRename()\">\n Applica\n </button>\n <button type=\"button\" class=\"fb-btn\" (click)=\"suggestNameFromLabel()\">Genera dalla label</button>\n <button type=\"button\" class=\"fb-btn fb-btn--ghost\" (click)=\"cancelRename()\">Annulla</button>\n </div>\n }\n </div>\n\n <div class=\"fb-field\">\n <label class=\"fb-field__label\">Descrizione</label>\n <textarea\n class=\"fb-textarea\"\n [value]=\"$any(node()?.description) || ''\"\n (input)=\"setDescription($any($event.target).value)\"\n ></textarea>\n </div>\n }\n\n <p class=\"fb-inspector__position\">\n Posizione sul canvas: {{ position().x }}, {{ position().y }}\n </p>\n\n <hr class=\"fb-inspector__divider\" />\n\n @if (isStart()) {\n <fb-start-inspector />\n } @else if (node()) {\n @switch (type()) {\n @case ('Screen') {\n <fb-screen-inspector [name]=\"selectedName()!\" [node]=\"node()!\" />\n }\n @case ('Assignment') {\n <fb-assignment-inspector [name]=\"selectedName()!\" [node]=\"node()!\" />\n }\n @case ('Decision') {\n <fb-decision-inspector [name]=\"selectedName()!\" [node]=\"node()!\" />\n }\n @case ('Loop') {\n <fb-loop-inspector [name]=\"selectedName()!\" [node]=\"node()!\" />\n }\n @case ('CollectionProcessor') {\n <fb-collection-processor-inspector [name]=\"selectedName()!\" [node]=\"node()!\" />\n }\n @case ('CustomError') {\n <fb-custom-error-inspector [name]=\"selectedName()!\" [node]=\"node()!\" />\n }\n @case ('Wait') {\n <fb-wait-inspector [name]=\"selectedName()!\" [node]=\"node()!\" />\n }\n @case ('RecordLookup') {\n <fb-record-lookup-inspector [name]=\"selectedName()!\" [node]=\"node()!\" />\n }\n @case ('RecordCreate') {\n <fb-record-write-inspector [name]=\"selectedName()!\" [node]=\"node()!\" type=\"RecordCreate\" />\n }\n @case ('RecordUpdate') {\n <fb-record-write-inspector [name]=\"selectedName()!\" [node]=\"node()!\" type=\"RecordUpdate\" />\n }\n @case ('RecordDelete') {\n <fb-record-write-inspector [name]=\"selectedName()!\" [node]=\"node()!\" type=\"RecordDelete\" />\n }\n @case ('RecordRollback') {\n <fb-record-rollback-inspector [name]=\"selectedName()!\" [node]=\"node()!\" />\n }\n @case ('ActionCall') {\n <fb-action-call-inspector [name]=\"selectedName()!\" [node]=\"node()!\" />\n }\n @case ('ScriptCall') {\n <fb-script-call-inspector [name]=\"selectedName()!\" [node]=\"node()!\" />\n }\n @case ('Subflow') {\n <fb-subflow-inspector [name]=\"selectedName()!\" [node]=\"node()!\" />\n }\n @case ('Transform') {\n <fb-transform-inspector [name]=\"selectedName()!\" [node]=\"node()!\" />\n }\n @default {\n <p class=\"fb-callout fb-callout--warn\">\n Nessun form specifico per il tipo \u00AB{{ type() }}\u00BB: i campi comuni sono modificabili qui sopra.\n </p>\n }\n }\n }\n\n @if (!isStart()) {\n <hr class=\"fb-inspector__divider\" />\n <div class=\"fb-field__row\">\n <button type=\"button\" class=\"fb-btn\" (click)=\"requestDuplicate()\">Duplica</button>\n <button type=\"button\" class=\"fb-btn fb-btn--danger\" (click)=\"requestRemove()\">Elimina</button>\n </div>\n }\n </div>\n}\n", styles: [":host{display:flex;flex-direction:column;height:100%;min-height:0;background:var(--fb-surface, #fff)}.fb-inspector__empty{margin:16px 12px;font-size:12px;line-height:1.5;color:var(--fb-text-muted, #667085)}.fb-inspector__header{display:flex;align-items:flex-start;justify-content:space-between;gap:8px;padding:10px 12px;border-bottom:1px solid var(--fb-border-subtle, #e6e9ee)}.fb-inspector__type{font-size:10px;font-weight:700;letter-spacing:.05em;text-transform:uppercase;color:var(--fb-text-subtle, #98a2b3)}.fb-inspector__title{margin:2px 0 0;font-size:14px;font-weight:600;color:var(--fb-text, #1d2939)}.fb-inspector__body{flex:1;min-height:0;overflow-y:auto;padding:14px}.fb-inspector__issues{margin:0 0 12px;padding:0;list-style:none}.fb-inspector__issue{margin-bottom:4px;padding:6px 8px;border-left:3px solid var(--fb-text-subtle, #98a2b3);border-radius:3px;background:var(--fb-surface-alt, #f8f9fb);font-size:11px;line-height:1.4;color:var(--fb-text, #1d2939)}.fb-inspector__issue--error{border-left-color:var(--fb-error, #c9372c)}.fb-inspector__issue--warning{border-left-color:var(--fb-warning, #b7791f)}.fb-inspector__issue-code{display:block;font-family:ui-monospace,SFMono-Regular,Menlo,monospace;font-size:9px;color:var(--fb-text-subtle, #98a2b3)}.fb-inspector__issue-path{display:block;margin-top:2px;font-family:ui-monospace,SFMono-Regular,Menlo,monospace;font-size:9px;color:var(--fb-text-muted, #667085)}.fb-inspector__position{margin:0;font-size:10px;color:var(--fb-text-subtle, #98a2b3)}.fb-inspector__divider{margin:12px 0;border:0;border-top:1px solid var(--fb-border-subtle, #e6e9ee)}\n"], dependencies: [{ kind: "component", type: ActionCallInspectorComponent, selector: "fb-action-call-inspector" }, { kind: "component", type: AssignmentInspectorComponent, selector: "fb-assignment-inspector" }, { kind: "component", type: CollectionProcessorInspectorComponent, selector: "fb-collection-processor-inspector" }, { kind: "component", type: CustomErrorInspectorComponent, selector: "fb-custom-error-inspector" }, { kind: "component", type: DecisionInspectorComponent, selector: "fb-decision-inspector" }, { kind: "component", type: LoopInspectorComponent, selector: "fb-loop-inspector" }, { kind: "component", type: RecordLookupInspectorComponent, selector: "fb-record-lookup-inspector" }, { kind: "component", type: RecordRollbackInspectorComponent, selector: "fb-record-rollback-inspector" }, { kind: "component", type: RecordWriteInspectorComponent, selector: "fb-record-write-inspector", inputs: ["type"] }, { kind: "component", type: ScreenInspectorComponent, selector: "fb-screen-inspector" }, { kind: "component", type: ScriptCallInspectorComponent, selector: "fb-script-call-inspector" }, { kind: "component", type: StartInspectorComponent, selector: "fb-start-inspector" }, { kind: "component", type: SubflowInspectorComponent, selector: "fb-subflow-inspector" }, { kind: "component", type: TransformInspectorComponent, selector: "fb-transform-inspector" }, { kind: "component", type: WaitInspectorComponent, selector: "fb-wait-inspector" }], changeDetection: i0.ChangeDetectionStrategy.OnPush });
6709
+ static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "20.3.27", type: ElementInspectorComponent, isStandalone: true, selector: "fb-element-inspector", inputs: { selectedName: { classPropertyName: "selectedName", publicName: "selectedName", isSignal: true, isRequired: false, transformFunction: null }, showHeader: { classPropertyName: "showHeader", publicName: "showHeader", isSignal: true, isRequired: false, transformFunction: null } }, outputs: { closed: "closed", removeRequested: "removeRequested", duplicateRequested: "duplicateRequested" }, ngImport: i0, template: "@if (!selectedName()) {\r\n <p class=\"fb-inspector__empty\">\r\n Seleziona un elemento sul canvas per modificarlo, oppure trascina un elemento dalla palette.\r\n </p>\r\n} @else {\r\n @if (showHeader()) {\r\n <header class=\"fb-inspector__header\">\r\n <div>\r\n <span class=\"fb-inspector__type\">{{ typeLabel() }}</span>\r\n <h2 class=\"fb-inspector__title\">\r\n {{ isStart() ? 'Avvio del flow' : node()?.label || selectedName() }}\r\n </h2>\r\n </div>\r\n <button type=\"button\" class=\"fb-btn fb-btn--ghost fb-btn--icon\" aria-label=\"Chiudi\" (click)=\"close()\">\r\n \u00D7\r\n </button>\r\n </header>\r\n }\r\n\r\n <div class=\"fb-inspector__body\">\r\n @if (issues().length) {\r\n <ul class=\"fb-inspector__issues\">\r\n <!-- `$index`: due rilievi con lo stesso codice e lo stesso path sono possibili. -->\r\n @for (issue of issues(); track $index) {\r\n <li\r\n class=\"fb-inspector__issue\"\r\n [class.fb-inspector__issue--error]=\"issue.severity === 'Error'\"\r\n [class.fb-inspector__issue--warning]=\"issue.severity === 'Warning'\"\r\n >\r\n <span class=\"fb-inspector__issue-code\">{{ issue.code }}</span>\r\n {{ issue.message }}\r\n @if (issue.path) {\r\n <span class=\"fb-inspector__issue-path\">{{ issue.path }}</span>\r\n }\r\n </li>\r\n }\r\n </ul>\r\n }\r\n\r\n @if (isUnsupported()) {\r\n <p class=\"fb-callout fb-callout--error\">\r\n Questo tipo di elemento non e\u2019 supportato dal motore: il flow che lo contiene non parte\r\n (ELEMENT_NOT_SUPPORTED). Non e\u2019 creabile dalla palette; se e\u2019 arrivato da un documento importato,\r\n va rimosso.\r\n </p>\r\n }\r\n\r\n @if (!isStart()) {\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]=\"$any(node()?.label) || ''\"\r\n placeholder=\"Nome mostrato sul canvas\"\r\n (input)=\"setLabel($any($event.target).value)\"\r\n />\r\n </div>\r\n\r\n <div class=\"fb-field\">\r\n <label class=\"fb-field__label\">Nome tecnico</label>\r\n @if (pendingName() === null) {\r\n <div class=\"fb-field__row\">\r\n <input class=\"fb-input fb-input--mono\" [value]=\"selectedName() || ''\" readonly />\r\n <button type=\"button\" class=\"fb-btn\" (click)=\"startRename()\">Rinomina</button>\r\n </div>\r\n <p class=\"fb-field__hint\">\r\n \u00C8 l\u2019identificatore con cui i riferimenti raggiungono questo elemento.\r\n @if (referenceCount() > 1) {\r\n Compare {{ referenceCount() }} volte nel documento.\r\n }\r\n </p>\r\n } @else {\r\n <input\r\n class=\"fb-input fb-input--mono\"\r\n [class.fb-input--invalid]=\"!!nameError()\"\r\n [value]=\"pendingName() || ''\"\r\n (input)=\"onPendingNameInput($any($event.target).value)\"\r\n />\r\n @if (nameError()) {\r\n <p class=\"fb-field__error\">{{ nameError() }}</p>\r\n } @else {\r\n <p class=\"fb-field__hint fb-field__hint--warn\">\r\n La rinomina riscrive tutti i riferimenti che puntano a questo elemento\r\n ({{ referenceCount() }} occorrenze): nessuna primitiva del backend lo fa, lo fa l\u2019editor.\r\n </p>\r\n }\r\n <div class=\"fb-field__row\">\r\n <button type=\"button\" class=\"fb-btn fb-btn--primary\" [disabled]=\"!canRename()\" (click)=\"applyRename()\">\r\n Applica\r\n </button>\r\n <button type=\"button\" class=\"fb-btn\" (click)=\"suggestNameFromLabel()\">Genera dalla label</button>\r\n <button type=\"button\" class=\"fb-btn fb-btn--ghost\" (click)=\"cancelRename()\">Annulla</button>\r\n </div>\r\n }\r\n </div>\r\n\r\n <div class=\"fb-field\">\r\n <label class=\"fb-field__label\">Descrizione</label>\r\n <textarea\r\n class=\"fb-textarea\"\r\n [value]=\"$any(node()?.description) || ''\"\r\n (input)=\"setDescription($any($event.target).value)\"\r\n ></textarea>\r\n </div>\r\n }\r\n\r\n <p class=\"fb-inspector__position\">\r\n Posizione sul canvas: {{ position().x }}, {{ position().y }}\r\n </p>\r\n\r\n <hr class=\"fb-inspector__divider\" />\r\n\r\n @if (isStart()) {\r\n <fb-start-inspector />\r\n } @else if (node()) {\r\n @switch (type()) {\r\n @case ('Screen') {\r\n <fb-screen-inspector [name]=\"selectedName()!\" [node]=\"node()!\" />\r\n }\r\n @case ('Assignment') {\r\n <fb-assignment-inspector [name]=\"selectedName()!\" [node]=\"node()!\" />\r\n }\r\n @case ('Decision') {\r\n <fb-decision-inspector [name]=\"selectedName()!\" [node]=\"node()!\" />\r\n }\r\n @case ('Loop') {\r\n <fb-loop-inspector [name]=\"selectedName()!\" [node]=\"node()!\" />\r\n }\r\n @case ('CollectionProcessor') {\r\n <fb-collection-processor-inspector [name]=\"selectedName()!\" [node]=\"node()!\" />\r\n }\r\n @case ('CustomError') {\r\n <fb-custom-error-inspector [name]=\"selectedName()!\" [node]=\"node()!\" />\r\n }\r\n @case ('Wait') {\r\n <fb-wait-inspector [name]=\"selectedName()!\" [node]=\"node()!\" />\r\n }\r\n @case ('RecordLookup') {\r\n <fb-record-lookup-inspector [name]=\"selectedName()!\" [node]=\"node()!\" />\r\n }\r\n @case ('RecordCreate') {\r\n <fb-record-write-inspector [name]=\"selectedName()!\" [node]=\"node()!\" type=\"RecordCreate\" />\r\n }\r\n @case ('RecordUpdate') {\r\n <fb-record-write-inspector [name]=\"selectedName()!\" [node]=\"node()!\" type=\"RecordUpdate\" />\r\n }\r\n @case ('RecordDelete') {\r\n <fb-record-write-inspector [name]=\"selectedName()!\" [node]=\"node()!\" type=\"RecordDelete\" />\r\n }\r\n @case ('RecordRollback') {\r\n <fb-record-rollback-inspector [name]=\"selectedName()!\" [node]=\"node()!\" />\r\n }\r\n @case ('ActionCall') {\r\n <fb-action-call-inspector [name]=\"selectedName()!\" [node]=\"node()!\" />\r\n }\r\n @case ('ScriptCall') {\r\n <fb-script-call-inspector [name]=\"selectedName()!\" [node]=\"node()!\" />\r\n }\r\n @case ('Subflow') {\r\n <fb-subflow-inspector [name]=\"selectedName()!\" [node]=\"node()!\" />\r\n }\r\n @case ('Transform') {\r\n <fb-transform-inspector [name]=\"selectedName()!\" [node]=\"node()!\" />\r\n }\r\n @case ('OrchestratedStage') {\r\n <fb-orchestrated-stage-inspector [name]=\"selectedName()!\" [node]=\"node()!\" />\r\n }\r\n @default {\r\n <p class=\"fb-callout fb-callout--warn\">\r\n Nessun form specifico per il tipo \u00AB{{ type() }}\u00BB: i campi comuni sono modificabili qui sopra.\r\n </p>\r\n }\r\n }\r\n }\r\n\r\n @if (!isStart()) {\r\n <hr class=\"fb-inspector__divider\" />\r\n <div class=\"fb-field__row\">\r\n <button type=\"button\" class=\"fb-btn\" (click)=\"requestDuplicate()\">Duplica</button>\r\n <button type=\"button\" class=\"fb-btn fb-btn--danger\" (click)=\"requestRemove()\">Elimina</button>\r\n </div>\r\n }\r\n </div>\r\n}\r\n", styles: [":host{display:flex;flex-direction:column;height:100%;min-height:0;background:var(--fb-surface, #fff)}.fb-inspector__empty{margin:16px 12px;font-size:12px;line-height:1.5;color:var(--fb-text-muted, #667085)}.fb-inspector__header{display:flex;align-items:flex-start;justify-content:space-between;gap:8px;padding:10px 12px;border-bottom:1px solid var(--fb-border-subtle, #e6e9ee)}.fb-inspector__type{font-size:10px;font-weight:700;letter-spacing:.05em;text-transform:uppercase;color:var(--fb-text-subtle, #98a2b3)}.fb-inspector__title{margin:2px 0 0;font-size:14px;font-weight:600;color:var(--fb-text, #1d2939)}.fb-inspector__body{flex:1;min-height:0;overflow-y:auto;padding:14px}.fb-inspector__issues{margin:0 0 12px;padding:0;list-style:none}.fb-inspector__issue{margin-bottom:4px;padding:6px 8px;border-left:3px solid var(--fb-text-subtle, #98a2b3);border-radius:3px;background:var(--fb-surface-alt, #f8f9fb);font-size:11px;line-height:1.4;color:var(--fb-text, #1d2939)}.fb-inspector__issue--error{border-left-color:var(--fb-error, #c9372c)}.fb-inspector__issue--warning{border-left-color:var(--fb-warning, #b7791f)}.fb-inspector__issue-code{display:block;font-family:ui-monospace,SFMono-Regular,Menlo,monospace;font-size:9px;color:var(--fb-text-subtle, #98a2b3)}.fb-inspector__issue-path{display:block;margin-top:2px;font-family:ui-monospace,SFMono-Regular,Menlo,monospace;font-size:9px;color:var(--fb-text-muted, #667085)}.fb-inspector__position{margin:0;font-size:10px;color:var(--fb-text-subtle, #98a2b3)}.fb-inspector__divider{margin:12px 0;border:0;border-top:1px solid var(--fb-border-subtle, #e6e9ee)}\n"], dependencies: [{ kind: "component", type: ActionCallInspectorComponent, selector: "fb-action-call-inspector" }, { kind: "component", type: AssignmentInspectorComponent, selector: "fb-assignment-inspector" }, { kind: "component", type: CollectionProcessorInspectorComponent, selector: "fb-collection-processor-inspector" }, { kind: "component", type: CustomErrorInspectorComponent, selector: "fb-custom-error-inspector" }, { kind: "component", type: DecisionInspectorComponent, selector: "fb-decision-inspector" }, { kind: "component", type: LoopInspectorComponent, selector: "fb-loop-inspector" }, { kind: "component", type: OrchestratedStageInspectorComponent, selector: "fb-orchestrated-stage-inspector" }, { kind: "component", type: RecordLookupInspectorComponent, selector: "fb-record-lookup-inspector" }, { kind: "component", type: RecordRollbackInspectorComponent, selector: "fb-record-rollback-inspector" }, { kind: "component", type: RecordWriteInspectorComponent, selector: "fb-record-write-inspector", inputs: ["type"] }, { kind: "component", type: ScreenInspectorComponent, selector: "fb-screen-inspector" }, { kind: "component", type: ScriptCallInspectorComponent, selector: "fb-script-call-inspector" }, { kind: "component", type: StartInspectorComponent, selector: "fb-start-inspector" }, { kind: "component", type: SubflowInspectorComponent, selector: "fb-subflow-inspector" }, { kind: "component", type: TransformInspectorComponent, selector: "fb-transform-inspector" }, { kind: "component", type: WaitInspectorComponent, selector: "fb-wait-inspector" }], changeDetection: i0.ChangeDetectionStrategy.OnPush });
5968
6710
  }
5969
6711
  i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.27", ngImport: i0, type: ElementInspectorComponent, decorators: [{
5970
6712
  type: Component,
@@ -5975,6 +6717,7 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.27", ngImpo
5975
6717
  CustomErrorInspectorComponent,
5976
6718
  DecisionInspectorComponent,
5977
6719
  LoopInspectorComponent,
6720
+ OrchestratedStageInspectorComponent,
5978
6721
  RecordLookupInspectorComponent,
5979
6722
  RecordRollbackInspectorComponent,
5980
6723
  RecordWriteInspectorComponent,
@@ -5984,7 +6727,7 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.27", ngImpo
5984
6727
  SubflowInspectorComponent,
5985
6728
  TransformInspectorComponent,
5986
6729
  WaitInspectorComponent,
5987
- ], changeDetection: ChangeDetectionStrategy.OnPush, template: "@if (!selectedName()) {\n <p class=\"fb-inspector__empty\">\n Seleziona un elemento sul canvas per modificarlo, oppure trascina un elemento dalla palette.\n </p>\n} @else {\n @if (showHeader()) {\n <header class=\"fb-inspector__header\">\n <div>\n <span class=\"fb-inspector__type\">{{ typeLabel() }}</span>\n <h2 class=\"fb-inspector__title\">\n {{ isStart() ? 'Avvio del flow' : node()?.label || selectedName() }}\n </h2>\n </div>\n <button type=\"button\" class=\"fb-btn fb-btn--ghost fb-btn--icon\" aria-label=\"Chiudi\" (click)=\"close()\">\n \u00D7\n </button>\n </header>\n }\n\n <div class=\"fb-inspector__body\">\n @if (issues().length) {\n <ul class=\"fb-inspector__issues\">\n <!-- `$index`: due rilievi con lo stesso codice e lo stesso path sono possibili. -->\n @for (issue of issues(); track $index) {\n <li\n class=\"fb-inspector__issue\"\n [class.fb-inspector__issue--error]=\"issue.severity === 'Error'\"\n [class.fb-inspector__issue--warning]=\"issue.severity === 'Warning'\"\n >\n <span class=\"fb-inspector__issue-code\">{{ issue.code }}</span>\n {{ issue.message }}\n @if (issue.path) {\n <span class=\"fb-inspector__issue-path\">{{ issue.path }}</span>\n }\n </li>\n }\n </ul>\n }\n\n @if (isUnsupported()) {\n <p class=\"fb-callout fb-callout--error\">\n Questo tipo di elemento non e\u2019 supportato dal motore: il flow che lo contiene non parte\n (ELEMENT_NOT_SUPPORTED). Non e\u2019 creabile dalla palette; se e\u2019 arrivato da un documento importato,\n va rimosso.\n </p>\n }\n\n @if (!isStart()) {\n <div class=\"fb-field\">\n <label class=\"fb-field__label\">Etichetta</label>\n <input\n class=\"fb-input\"\n [value]=\"$any(node()?.label) || ''\"\n placeholder=\"Nome mostrato sul canvas\"\n (input)=\"setLabel($any($event.target).value)\"\n />\n </div>\n\n <div class=\"fb-field\">\n <label class=\"fb-field__label\">Nome tecnico</label>\n @if (pendingName() === null) {\n <div class=\"fb-field__row\">\n <input class=\"fb-input fb-input--mono\" [value]=\"selectedName() || ''\" readonly />\n <button type=\"button\" class=\"fb-btn\" (click)=\"startRename()\">Rinomina</button>\n </div>\n <p class=\"fb-field__hint\">\n \u00C8 l\u2019identificatore con cui i riferimenti raggiungono questo elemento.\n @if (referenceCount() > 1) {\n Compare {{ referenceCount() }} volte nel documento.\n }\n </p>\n } @else {\n <input\n class=\"fb-input fb-input--mono\"\n [class.fb-input--invalid]=\"!!nameError()\"\n [value]=\"pendingName() || ''\"\n (input)=\"onPendingNameInput($any($event.target).value)\"\n />\n @if (nameError()) {\n <p class=\"fb-field__error\">{{ nameError() }}</p>\n } @else {\n <p class=\"fb-field__hint fb-field__hint--warn\">\n La rinomina riscrive tutti i riferimenti che puntano a questo elemento\n ({{ referenceCount() }} occorrenze): nessuna primitiva del backend lo fa, lo fa l\u2019editor.\n </p>\n }\n <div class=\"fb-field__row\">\n <button type=\"button\" class=\"fb-btn fb-btn--primary\" [disabled]=\"!canRename()\" (click)=\"applyRename()\">\n Applica\n </button>\n <button type=\"button\" class=\"fb-btn\" (click)=\"suggestNameFromLabel()\">Genera dalla label</button>\n <button type=\"button\" class=\"fb-btn fb-btn--ghost\" (click)=\"cancelRename()\">Annulla</button>\n </div>\n }\n </div>\n\n <div class=\"fb-field\">\n <label class=\"fb-field__label\">Descrizione</label>\n <textarea\n class=\"fb-textarea\"\n [value]=\"$any(node()?.description) || ''\"\n (input)=\"setDescription($any($event.target).value)\"\n ></textarea>\n </div>\n }\n\n <p class=\"fb-inspector__position\">\n Posizione sul canvas: {{ position().x }}, {{ position().y }}\n </p>\n\n <hr class=\"fb-inspector__divider\" />\n\n @if (isStart()) {\n <fb-start-inspector />\n } @else if (node()) {\n @switch (type()) {\n @case ('Screen') {\n <fb-screen-inspector [name]=\"selectedName()!\" [node]=\"node()!\" />\n }\n @case ('Assignment') {\n <fb-assignment-inspector [name]=\"selectedName()!\" [node]=\"node()!\" />\n }\n @case ('Decision') {\n <fb-decision-inspector [name]=\"selectedName()!\" [node]=\"node()!\" />\n }\n @case ('Loop') {\n <fb-loop-inspector [name]=\"selectedName()!\" [node]=\"node()!\" />\n }\n @case ('CollectionProcessor') {\n <fb-collection-processor-inspector [name]=\"selectedName()!\" [node]=\"node()!\" />\n }\n @case ('CustomError') {\n <fb-custom-error-inspector [name]=\"selectedName()!\" [node]=\"node()!\" />\n }\n @case ('Wait') {\n <fb-wait-inspector [name]=\"selectedName()!\" [node]=\"node()!\" />\n }\n @case ('RecordLookup') {\n <fb-record-lookup-inspector [name]=\"selectedName()!\" [node]=\"node()!\" />\n }\n @case ('RecordCreate') {\n <fb-record-write-inspector [name]=\"selectedName()!\" [node]=\"node()!\" type=\"RecordCreate\" />\n }\n @case ('RecordUpdate') {\n <fb-record-write-inspector [name]=\"selectedName()!\" [node]=\"node()!\" type=\"RecordUpdate\" />\n }\n @case ('RecordDelete') {\n <fb-record-write-inspector [name]=\"selectedName()!\" [node]=\"node()!\" type=\"RecordDelete\" />\n }\n @case ('RecordRollback') {\n <fb-record-rollback-inspector [name]=\"selectedName()!\" [node]=\"node()!\" />\n }\n @case ('ActionCall') {\n <fb-action-call-inspector [name]=\"selectedName()!\" [node]=\"node()!\" />\n }\n @case ('ScriptCall') {\n <fb-script-call-inspector [name]=\"selectedName()!\" [node]=\"node()!\" />\n }\n @case ('Subflow') {\n <fb-subflow-inspector [name]=\"selectedName()!\" [node]=\"node()!\" />\n }\n @case ('Transform') {\n <fb-transform-inspector [name]=\"selectedName()!\" [node]=\"node()!\" />\n }\n @default {\n <p class=\"fb-callout fb-callout--warn\">\n Nessun form specifico per il tipo \u00AB{{ type() }}\u00BB: i campi comuni sono modificabili qui sopra.\n </p>\n }\n }\n }\n\n @if (!isStart()) {\n <hr class=\"fb-inspector__divider\" />\n <div class=\"fb-field__row\">\n <button type=\"button\" class=\"fb-btn\" (click)=\"requestDuplicate()\">Duplica</button>\n <button type=\"button\" class=\"fb-btn fb-btn--danger\" (click)=\"requestRemove()\">Elimina</button>\n </div>\n }\n </div>\n}\n", styles: [":host{display:flex;flex-direction:column;height:100%;min-height:0;background:var(--fb-surface, #fff)}.fb-inspector__empty{margin:16px 12px;font-size:12px;line-height:1.5;color:var(--fb-text-muted, #667085)}.fb-inspector__header{display:flex;align-items:flex-start;justify-content:space-between;gap:8px;padding:10px 12px;border-bottom:1px solid var(--fb-border-subtle, #e6e9ee)}.fb-inspector__type{font-size:10px;font-weight:700;letter-spacing:.05em;text-transform:uppercase;color:var(--fb-text-subtle, #98a2b3)}.fb-inspector__title{margin:2px 0 0;font-size:14px;font-weight:600;color:var(--fb-text, #1d2939)}.fb-inspector__body{flex:1;min-height:0;overflow-y:auto;padding:14px}.fb-inspector__issues{margin:0 0 12px;padding:0;list-style:none}.fb-inspector__issue{margin-bottom:4px;padding:6px 8px;border-left:3px solid var(--fb-text-subtle, #98a2b3);border-radius:3px;background:var(--fb-surface-alt, #f8f9fb);font-size:11px;line-height:1.4;color:var(--fb-text, #1d2939)}.fb-inspector__issue--error{border-left-color:var(--fb-error, #c9372c)}.fb-inspector__issue--warning{border-left-color:var(--fb-warning, #b7791f)}.fb-inspector__issue-code{display:block;font-family:ui-monospace,SFMono-Regular,Menlo,monospace;font-size:9px;color:var(--fb-text-subtle, #98a2b3)}.fb-inspector__issue-path{display:block;margin-top:2px;font-family:ui-monospace,SFMono-Regular,Menlo,monospace;font-size:9px;color:var(--fb-text-muted, #667085)}.fb-inspector__position{margin:0;font-size:10px;color:var(--fb-text-subtle, #98a2b3)}.fb-inspector__divider{margin:12px 0;border:0;border-top:1px solid var(--fb-border-subtle, #e6e9ee)}\n"] }]
6730
+ ], changeDetection: ChangeDetectionStrategy.OnPush, template: "@if (!selectedName()) {\r\n <p class=\"fb-inspector__empty\">\r\n Seleziona un elemento sul canvas per modificarlo, oppure trascina un elemento dalla palette.\r\n </p>\r\n} @else {\r\n @if (showHeader()) {\r\n <header class=\"fb-inspector__header\">\r\n <div>\r\n <span class=\"fb-inspector__type\">{{ typeLabel() }}</span>\r\n <h2 class=\"fb-inspector__title\">\r\n {{ isStart() ? 'Avvio del flow' : node()?.label || selectedName() }}\r\n </h2>\r\n </div>\r\n <button type=\"button\" class=\"fb-btn fb-btn--ghost fb-btn--icon\" aria-label=\"Chiudi\" (click)=\"close()\">\r\n \u00D7\r\n </button>\r\n </header>\r\n }\r\n\r\n <div class=\"fb-inspector__body\">\r\n @if (issues().length) {\r\n <ul class=\"fb-inspector__issues\">\r\n <!-- `$index`: due rilievi con lo stesso codice e lo stesso path sono possibili. -->\r\n @for (issue of issues(); track $index) {\r\n <li\r\n class=\"fb-inspector__issue\"\r\n [class.fb-inspector__issue--error]=\"issue.severity === 'Error'\"\r\n [class.fb-inspector__issue--warning]=\"issue.severity === 'Warning'\"\r\n >\r\n <span class=\"fb-inspector__issue-code\">{{ issue.code }}</span>\r\n {{ issue.message }}\r\n @if (issue.path) {\r\n <span class=\"fb-inspector__issue-path\">{{ issue.path }}</span>\r\n }\r\n </li>\r\n }\r\n </ul>\r\n }\r\n\r\n @if (isUnsupported()) {\r\n <p class=\"fb-callout fb-callout--error\">\r\n Questo tipo di elemento non e\u2019 supportato dal motore: il flow che lo contiene non parte\r\n (ELEMENT_NOT_SUPPORTED). Non e\u2019 creabile dalla palette; se e\u2019 arrivato da un documento importato,\r\n va rimosso.\r\n </p>\r\n }\r\n\r\n @if (!isStart()) {\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]=\"$any(node()?.label) || ''\"\r\n placeholder=\"Nome mostrato sul canvas\"\r\n (input)=\"setLabel($any($event.target).value)\"\r\n />\r\n </div>\r\n\r\n <div class=\"fb-field\">\r\n <label class=\"fb-field__label\">Nome tecnico</label>\r\n @if (pendingName() === null) {\r\n <div class=\"fb-field__row\">\r\n <input class=\"fb-input fb-input--mono\" [value]=\"selectedName() || ''\" readonly />\r\n <button type=\"button\" class=\"fb-btn\" (click)=\"startRename()\">Rinomina</button>\r\n </div>\r\n <p class=\"fb-field__hint\">\r\n \u00C8 l\u2019identificatore con cui i riferimenti raggiungono questo elemento.\r\n @if (referenceCount() > 1) {\r\n Compare {{ referenceCount() }} volte nel documento.\r\n }\r\n </p>\r\n } @else {\r\n <input\r\n class=\"fb-input fb-input--mono\"\r\n [class.fb-input--invalid]=\"!!nameError()\"\r\n [value]=\"pendingName() || ''\"\r\n (input)=\"onPendingNameInput($any($event.target).value)\"\r\n />\r\n @if (nameError()) {\r\n <p class=\"fb-field__error\">{{ nameError() }}</p>\r\n } @else {\r\n <p class=\"fb-field__hint fb-field__hint--warn\">\r\n La rinomina riscrive tutti i riferimenti che puntano a questo elemento\r\n ({{ referenceCount() }} occorrenze): nessuna primitiva del backend lo fa, lo fa l\u2019editor.\r\n </p>\r\n }\r\n <div class=\"fb-field__row\">\r\n <button type=\"button\" class=\"fb-btn fb-btn--primary\" [disabled]=\"!canRename()\" (click)=\"applyRename()\">\r\n Applica\r\n </button>\r\n <button type=\"button\" class=\"fb-btn\" (click)=\"suggestNameFromLabel()\">Genera dalla label</button>\r\n <button type=\"button\" class=\"fb-btn fb-btn--ghost\" (click)=\"cancelRename()\">Annulla</button>\r\n </div>\r\n }\r\n </div>\r\n\r\n <div class=\"fb-field\">\r\n <label class=\"fb-field__label\">Descrizione</label>\r\n <textarea\r\n class=\"fb-textarea\"\r\n [value]=\"$any(node()?.description) || ''\"\r\n (input)=\"setDescription($any($event.target).value)\"\r\n ></textarea>\r\n </div>\r\n }\r\n\r\n <p class=\"fb-inspector__position\">\r\n Posizione sul canvas: {{ position().x }}, {{ position().y }}\r\n </p>\r\n\r\n <hr class=\"fb-inspector__divider\" />\r\n\r\n @if (isStart()) {\r\n <fb-start-inspector />\r\n } @else if (node()) {\r\n @switch (type()) {\r\n @case ('Screen') {\r\n <fb-screen-inspector [name]=\"selectedName()!\" [node]=\"node()!\" />\r\n }\r\n @case ('Assignment') {\r\n <fb-assignment-inspector [name]=\"selectedName()!\" [node]=\"node()!\" />\r\n }\r\n @case ('Decision') {\r\n <fb-decision-inspector [name]=\"selectedName()!\" [node]=\"node()!\" />\r\n }\r\n @case ('Loop') {\r\n <fb-loop-inspector [name]=\"selectedName()!\" [node]=\"node()!\" />\r\n }\r\n @case ('CollectionProcessor') {\r\n <fb-collection-processor-inspector [name]=\"selectedName()!\" [node]=\"node()!\" />\r\n }\r\n @case ('CustomError') {\r\n <fb-custom-error-inspector [name]=\"selectedName()!\" [node]=\"node()!\" />\r\n }\r\n @case ('Wait') {\r\n <fb-wait-inspector [name]=\"selectedName()!\" [node]=\"node()!\" />\r\n }\r\n @case ('RecordLookup') {\r\n <fb-record-lookup-inspector [name]=\"selectedName()!\" [node]=\"node()!\" />\r\n }\r\n @case ('RecordCreate') {\r\n <fb-record-write-inspector [name]=\"selectedName()!\" [node]=\"node()!\" type=\"RecordCreate\" />\r\n }\r\n @case ('RecordUpdate') {\r\n <fb-record-write-inspector [name]=\"selectedName()!\" [node]=\"node()!\" type=\"RecordUpdate\" />\r\n }\r\n @case ('RecordDelete') {\r\n <fb-record-write-inspector [name]=\"selectedName()!\" [node]=\"node()!\" type=\"RecordDelete\" />\r\n }\r\n @case ('RecordRollback') {\r\n <fb-record-rollback-inspector [name]=\"selectedName()!\" [node]=\"node()!\" />\r\n }\r\n @case ('ActionCall') {\r\n <fb-action-call-inspector [name]=\"selectedName()!\" [node]=\"node()!\" />\r\n }\r\n @case ('ScriptCall') {\r\n <fb-script-call-inspector [name]=\"selectedName()!\" [node]=\"node()!\" />\r\n }\r\n @case ('Subflow') {\r\n <fb-subflow-inspector [name]=\"selectedName()!\" [node]=\"node()!\" />\r\n }\r\n @case ('Transform') {\r\n <fb-transform-inspector [name]=\"selectedName()!\" [node]=\"node()!\" />\r\n }\r\n @case ('OrchestratedStage') {\r\n <fb-orchestrated-stage-inspector [name]=\"selectedName()!\" [node]=\"node()!\" />\r\n }\r\n @default {\r\n <p class=\"fb-callout fb-callout--warn\">\r\n Nessun form specifico per il tipo \u00AB{{ type() }}\u00BB: i campi comuni sono modificabili qui sopra.\r\n </p>\r\n }\r\n }\r\n }\r\n\r\n @if (!isStart()) {\r\n <hr class=\"fb-inspector__divider\" />\r\n <div class=\"fb-field__row\">\r\n <button type=\"button\" class=\"fb-btn\" (click)=\"requestDuplicate()\">Duplica</button>\r\n <button type=\"button\" class=\"fb-btn fb-btn--danger\" (click)=\"requestRemove()\">Elimina</button>\r\n </div>\r\n }\r\n </div>\r\n}\r\n", styles: [":host{display:flex;flex-direction:column;height:100%;min-height:0;background:var(--fb-surface, #fff)}.fb-inspector__empty{margin:16px 12px;font-size:12px;line-height:1.5;color:var(--fb-text-muted, #667085)}.fb-inspector__header{display:flex;align-items:flex-start;justify-content:space-between;gap:8px;padding:10px 12px;border-bottom:1px solid var(--fb-border-subtle, #e6e9ee)}.fb-inspector__type{font-size:10px;font-weight:700;letter-spacing:.05em;text-transform:uppercase;color:var(--fb-text-subtle, #98a2b3)}.fb-inspector__title{margin:2px 0 0;font-size:14px;font-weight:600;color:var(--fb-text, #1d2939)}.fb-inspector__body{flex:1;min-height:0;overflow-y:auto;padding:14px}.fb-inspector__issues{margin:0 0 12px;padding:0;list-style:none}.fb-inspector__issue{margin-bottom:4px;padding:6px 8px;border-left:3px solid var(--fb-text-subtle, #98a2b3);border-radius:3px;background:var(--fb-surface-alt, #f8f9fb);font-size:11px;line-height:1.4;color:var(--fb-text, #1d2939)}.fb-inspector__issue--error{border-left-color:var(--fb-error, #c9372c)}.fb-inspector__issue--warning{border-left-color:var(--fb-warning, #b7791f)}.fb-inspector__issue-code{display:block;font-family:ui-monospace,SFMono-Regular,Menlo,monospace;font-size:9px;color:var(--fb-text-subtle, #98a2b3)}.fb-inspector__issue-path{display:block;margin-top:2px;font-family:ui-monospace,SFMono-Regular,Menlo,monospace;font-size:9px;color:var(--fb-text-muted, #667085)}.fb-inspector__position{margin:0;font-size:10px;color:var(--fb-text-subtle, #98a2b3)}.fb-inspector__divider{margin:12px 0;border:0;border-top:1px solid var(--fb-border-subtle, #e6e9ee)}\n"] }]
5988
6731
  }], propDecorators: { selectedName: [{ type: i0.Input, args: [{ isSignal: true, alias: "selectedName", required: false }] }], showHeader: [{ type: i0.Input, args: [{ isSignal: true, alias: "showHeader", required: false }] }], closed: [{ type: i0.Output, args: ["closed"] }], removeRequested: [{ type: i0.Output, args: ["removeRequested"] }], duplicateRequested: [{ type: i0.Output, args: ["duplicateRequested"] }] } });
5989
6732
 
5990
6733
  /**
@@ -6048,11 +6791,11 @@ class ElementDialogComponent {
6048
6791
  this.duplicateRequested.emit(name);
6049
6792
  }
6050
6793
  static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.3.27", ngImport: i0, type: ElementDialogComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
6051
- static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.2.0", version: "20.3.27", type: ElementDialogComponent, isStandalone: true, selector: "fb-element-dialog", inputs: { selectedName: { classPropertyName: "selectedName", publicName: "selectedName", isSignal: true, isRequired: false, transformFunction: null } }, outputs: { closed: "closed", removeRequested: "removeRequested", duplicateRequested: "duplicateRequested" }, viewQueries: [{ propertyName: "panel", first: true, predicate: ["panel"], descendants: true, isSignal: true }], ngImport: i0, template: "<!--\n Il backdrop chiude: e' il gesto che tutti si aspettano. Il pannello ferma la propagazione\n del click, altrimenti configurare un campo chiuderebbe la dialog.\n-->\n<div class=\"fb-dialog__backdrop\" (click)=\"close()\"></div>\n\n<div\n #panel\n class=\"fb-dialog__panel\"\n role=\"dialog\"\n aria-modal=\"true\"\n [attr.aria-label]=\"typeLabel() + ': ' + title()\"\n tabindex=\"-1\"\n (click)=\"$event.stopPropagation()\"\n (keydown.escape)=\"close()\"\n>\n <header class=\"fb-dialog__head\">\n <div class=\"fb-dialog__identity\">\n <span class=\"fb-dialog__type\">{{ typeLabel() }}</span>\n <h2 class=\"fb-dialog__title\">{{ title() }}</h2>\n </div>\n <button type=\"button\" class=\"fb-btn fb-btn--ghost fb-btn--icon\" aria-label=\"Chiudi\" (click)=\"close()\">\u00D7</button>\n </header>\n\n <div class=\"fb-dialog__body\">\n <!-- L'intestazione dell'inspector qui e' quella della dialog: non si ripete. -->\n <fb-element-inspector\n [selectedName]=\"selectedName()\"\n [showHeader]=\"false\"\n (removeRequested)=\"onRemoveRequested($event)\"\n (duplicateRequested)=\"onDuplicateRequested($event)\"\n (closed)=\"close()\"\n />\n </div>\n\n <footer class=\"fb-dialog__foot\">\n <span class=\"fb-dialog__note\">\n Le modifiche sono gi\u00E0 nel documento: per tornare indietro c\u2019\u00E8 l\u2019annulla dell\u2019editor.\n </span>\n <button type=\"button\" class=\"fb-btn fb-btn--primary\" (click)=\"close()\">Fatto</button>\n </footer>\n</div>\n", styles: [":host{position:absolute;inset:0;z-index:30;display:grid;place-items:center;padding:24px}.fb-dialog__backdrop{position:absolute;inset:0;background:#10182852;-webkit-backdrop-filter:blur(1px);backdrop-filter:blur(1px)}.fb-dialog__panel{position:relative;display:flex;flex-direction:column;width:min(760px,100%);max-height:100%;border:1px solid var(--fb-border, #e2e5eb);border-radius:var(--fb-radius-lg, 12px);background:var(--fb-surface, #fff);box-shadow:var(--fb-shadow-lg, 0 18px 44px rgb(16 24 40 / 18%));outline:none;overflow:hidden}.fb-dialog__head{display:flex;align-items:center;justify-content:space-between;gap:12px;padding:12px 14px;border-bottom:1px solid var(--fb-border-subtle, #eef0f4)}.fb-dialog__identity{min-width:0}.fb-dialog__type{display:block;font-size:10px;font-weight:700;letter-spacing:.06em;text-transform:uppercase;color:var(--fb-text-subtle, #98a2b3)}.fb-dialog__title{margin:1px 0 0;font-size:15px;font-weight:600;color:var(--fb-text, #1a1c23);overflow:hidden;text-overflow:ellipsis;white-space:nowrap}.fb-dialog__body{flex:1;min-height:0;overflow-y:auto;overscroll-behavior:contain}.fb-dialog__foot{display:flex;align-items:center;justify-content:space-between;gap:12px;padding:10px 14px;border-top:1px solid var(--fb-border-subtle, #eef0f4);background:var(--fb-surface-alt, #f7f8fa)}.fb-dialog__note{font-size:11px;color:var(--fb-text-muted, #6b7086)}@media(max-height:620px){:host{padding:10px}}\n"], dependencies: [{ kind: "component", type: ElementInspectorComponent, selector: "fb-element-inspector", inputs: ["selectedName", "showHeader"], outputs: ["closed", "removeRequested", "duplicateRequested"] }], changeDetection: i0.ChangeDetectionStrategy.OnPush });
6794
+ static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.2.0", version: "20.3.27", type: ElementDialogComponent, isStandalone: true, selector: "fb-element-dialog", inputs: { selectedName: { classPropertyName: "selectedName", publicName: "selectedName", isSignal: true, isRequired: false, transformFunction: null } }, outputs: { closed: "closed", removeRequested: "removeRequested", duplicateRequested: "duplicateRequested" }, viewQueries: [{ propertyName: "panel", first: true, predicate: ["panel"], descendants: true, isSignal: true }], ngImport: i0, template: "<!--\r\n Il backdrop chiude: e' il gesto che tutti si aspettano. Il pannello ferma la propagazione\r\n del click, altrimenti configurare un campo chiuderebbe la dialog.\r\n-->\r\n<div class=\"fb-dialog__backdrop\" (click)=\"close()\"></div>\r\n\r\n<div\r\n #panel\r\n class=\"fb-dialog__panel\"\r\n role=\"dialog\"\r\n aria-modal=\"true\"\r\n [attr.aria-label]=\"typeLabel() + ': ' + title()\"\r\n tabindex=\"-1\"\r\n (click)=\"$event.stopPropagation()\"\r\n (keydown.escape)=\"close()\"\r\n>\r\n <header class=\"fb-dialog__head\">\r\n <div class=\"fb-dialog__identity\">\r\n <span class=\"fb-dialog__type\">{{ typeLabel() }}</span>\r\n <h2 class=\"fb-dialog__title\">{{ title() }}</h2>\r\n </div>\r\n <button type=\"button\" class=\"fb-btn fb-btn--ghost fb-btn--icon\" aria-label=\"Chiudi\" (click)=\"close()\">\u00D7</button>\r\n </header>\r\n\r\n <div class=\"fb-dialog__body\">\r\n <!-- L'intestazione dell'inspector qui e' quella della dialog: non si ripete. -->\r\n <fb-element-inspector\r\n [selectedName]=\"selectedName()\"\r\n [showHeader]=\"false\"\r\n (removeRequested)=\"onRemoveRequested($event)\"\r\n (duplicateRequested)=\"onDuplicateRequested($event)\"\r\n (closed)=\"close()\"\r\n />\r\n </div>\r\n\r\n <footer class=\"fb-dialog__foot\">\r\n <span class=\"fb-dialog__note\">\r\n Le modifiche sono gi\u00E0 nel documento: per tornare indietro c\u2019\u00E8 l\u2019annulla dell\u2019editor.\r\n </span>\r\n <button type=\"button\" class=\"fb-btn fb-btn--primary\" (click)=\"close()\">Fatto</button>\r\n </footer>\r\n</div>\r\n", styles: [":host{position:absolute;inset:0;z-index:30;display:grid;place-items:center;padding:24px}.fb-dialog__backdrop{position:absolute;inset:0;background:#10182852;-webkit-backdrop-filter:blur(1px);backdrop-filter:blur(1px)}.fb-dialog__panel{position:relative;display:flex;flex-direction:column;width:min(760px,100%);max-height:100%;border:1px solid var(--fb-border, #e2e5eb);border-radius:var(--fb-radius-lg, 12px);background:var(--fb-surface, #fff);box-shadow:var(--fb-shadow-lg, 0 18px 44px rgb(16 24 40 / 18%));outline:none;overflow:hidden}.fb-dialog__head{display:flex;align-items:center;justify-content:space-between;gap:12px;padding:12px 14px;border-bottom:1px solid var(--fb-border-subtle, #eef0f4)}.fb-dialog__identity{min-width:0}.fb-dialog__type{display:block;font-size:10px;font-weight:700;letter-spacing:.06em;text-transform:uppercase;color:var(--fb-text-subtle, #98a2b3)}.fb-dialog__title{margin:1px 0 0;font-size:15px;font-weight:600;color:var(--fb-text, #1a1c23);overflow:hidden;text-overflow:ellipsis;white-space:nowrap}.fb-dialog__body{flex:1;min-height:0;overflow-y:auto;overscroll-behavior:contain}.fb-dialog__foot{display:flex;align-items:center;justify-content:space-between;gap:12px;padding:10px 14px;border-top:1px solid var(--fb-border-subtle, #eef0f4);background:var(--fb-surface-alt, #f7f8fa)}.fb-dialog__note{font-size:11px;color:var(--fb-text-muted, #6b7086)}@media(max-height:620px){:host{padding:10px}}\n"], dependencies: [{ kind: "component", type: ElementInspectorComponent, selector: "fb-element-inspector", inputs: ["selectedName", "showHeader"], outputs: ["closed", "removeRequested", "duplicateRequested"] }], changeDetection: i0.ChangeDetectionStrategy.OnPush });
6052
6795
  }
6053
6796
  i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.27", ngImport: i0, type: ElementDialogComponent, decorators: [{
6054
6797
  type: Component,
6055
- args: [{ selector: 'fb-element-dialog', standalone: true, imports: [ElementInspectorComponent], changeDetection: ChangeDetectionStrategy.OnPush, template: "<!--\n Il backdrop chiude: e' il gesto che tutti si aspettano. Il pannello ferma la propagazione\n del click, altrimenti configurare un campo chiuderebbe la dialog.\n-->\n<div class=\"fb-dialog__backdrop\" (click)=\"close()\"></div>\n\n<div\n #panel\n class=\"fb-dialog__panel\"\n role=\"dialog\"\n aria-modal=\"true\"\n [attr.aria-label]=\"typeLabel() + ': ' + title()\"\n tabindex=\"-1\"\n (click)=\"$event.stopPropagation()\"\n (keydown.escape)=\"close()\"\n>\n <header class=\"fb-dialog__head\">\n <div class=\"fb-dialog__identity\">\n <span class=\"fb-dialog__type\">{{ typeLabel() }}</span>\n <h2 class=\"fb-dialog__title\">{{ title() }}</h2>\n </div>\n <button type=\"button\" class=\"fb-btn fb-btn--ghost fb-btn--icon\" aria-label=\"Chiudi\" (click)=\"close()\">\u00D7</button>\n </header>\n\n <div class=\"fb-dialog__body\">\n <!-- L'intestazione dell'inspector qui e' quella della dialog: non si ripete. -->\n <fb-element-inspector\n [selectedName]=\"selectedName()\"\n [showHeader]=\"false\"\n (removeRequested)=\"onRemoveRequested($event)\"\n (duplicateRequested)=\"onDuplicateRequested($event)\"\n (closed)=\"close()\"\n />\n </div>\n\n <footer class=\"fb-dialog__foot\">\n <span class=\"fb-dialog__note\">\n Le modifiche sono gi\u00E0 nel documento: per tornare indietro c\u2019\u00E8 l\u2019annulla dell\u2019editor.\n </span>\n <button type=\"button\" class=\"fb-btn fb-btn--primary\" (click)=\"close()\">Fatto</button>\n </footer>\n</div>\n", styles: [":host{position:absolute;inset:0;z-index:30;display:grid;place-items:center;padding:24px}.fb-dialog__backdrop{position:absolute;inset:0;background:#10182852;-webkit-backdrop-filter:blur(1px);backdrop-filter:blur(1px)}.fb-dialog__panel{position:relative;display:flex;flex-direction:column;width:min(760px,100%);max-height:100%;border:1px solid var(--fb-border, #e2e5eb);border-radius:var(--fb-radius-lg, 12px);background:var(--fb-surface, #fff);box-shadow:var(--fb-shadow-lg, 0 18px 44px rgb(16 24 40 / 18%));outline:none;overflow:hidden}.fb-dialog__head{display:flex;align-items:center;justify-content:space-between;gap:12px;padding:12px 14px;border-bottom:1px solid var(--fb-border-subtle, #eef0f4)}.fb-dialog__identity{min-width:0}.fb-dialog__type{display:block;font-size:10px;font-weight:700;letter-spacing:.06em;text-transform:uppercase;color:var(--fb-text-subtle, #98a2b3)}.fb-dialog__title{margin:1px 0 0;font-size:15px;font-weight:600;color:var(--fb-text, #1a1c23);overflow:hidden;text-overflow:ellipsis;white-space:nowrap}.fb-dialog__body{flex:1;min-height:0;overflow-y:auto;overscroll-behavior:contain}.fb-dialog__foot{display:flex;align-items:center;justify-content:space-between;gap:12px;padding:10px 14px;border-top:1px solid var(--fb-border-subtle, #eef0f4);background:var(--fb-surface-alt, #f7f8fa)}.fb-dialog__note{font-size:11px;color:var(--fb-text-muted, #6b7086)}@media(max-height:620px){:host{padding:10px}}\n"] }]
6798
+ args: [{ selector: 'fb-element-dialog', standalone: true, imports: [ElementInspectorComponent], changeDetection: ChangeDetectionStrategy.OnPush, template: "<!--\r\n Il backdrop chiude: e' il gesto che tutti si aspettano. Il pannello ferma la propagazione\r\n del click, altrimenti configurare un campo chiuderebbe la dialog.\r\n-->\r\n<div class=\"fb-dialog__backdrop\" (click)=\"close()\"></div>\r\n\r\n<div\r\n #panel\r\n class=\"fb-dialog__panel\"\r\n role=\"dialog\"\r\n aria-modal=\"true\"\r\n [attr.aria-label]=\"typeLabel() + ': ' + title()\"\r\n tabindex=\"-1\"\r\n (click)=\"$event.stopPropagation()\"\r\n (keydown.escape)=\"close()\"\r\n>\r\n <header class=\"fb-dialog__head\">\r\n <div class=\"fb-dialog__identity\">\r\n <span class=\"fb-dialog__type\">{{ typeLabel() }}</span>\r\n <h2 class=\"fb-dialog__title\">{{ title() }}</h2>\r\n </div>\r\n <button type=\"button\" class=\"fb-btn fb-btn--ghost fb-btn--icon\" aria-label=\"Chiudi\" (click)=\"close()\">\u00D7</button>\r\n </header>\r\n\r\n <div class=\"fb-dialog__body\">\r\n <!-- L'intestazione dell'inspector qui e' quella della dialog: non si ripete. -->\r\n <fb-element-inspector\r\n [selectedName]=\"selectedName()\"\r\n [showHeader]=\"false\"\r\n (removeRequested)=\"onRemoveRequested($event)\"\r\n (duplicateRequested)=\"onDuplicateRequested($event)\"\r\n (closed)=\"close()\"\r\n />\r\n </div>\r\n\r\n <footer class=\"fb-dialog__foot\">\r\n <span class=\"fb-dialog__note\">\r\n Le modifiche sono gi\u00E0 nel documento: per tornare indietro c\u2019\u00E8 l\u2019annulla dell\u2019editor.\r\n </span>\r\n <button type=\"button\" class=\"fb-btn fb-btn--primary\" (click)=\"close()\">Fatto</button>\r\n </footer>\r\n</div>\r\n", styles: [":host{position:absolute;inset:0;z-index:30;display:grid;place-items:center;padding:24px}.fb-dialog__backdrop{position:absolute;inset:0;background:#10182852;-webkit-backdrop-filter:blur(1px);backdrop-filter:blur(1px)}.fb-dialog__panel{position:relative;display:flex;flex-direction:column;width:min(760px,100%);max-height:100%;border:1px solid var(--fb-border, #e2e5eb);border-radius:var(--fb-radius-lg, 12px);background:var(--fb-surface, #fff);box-shadow:var(--fb-shadow-lg, 0 18px 44px rgb(16 24 40 / 18%));outline:none;overflow:hidden}.fb-dialog__head{display:flex;align-items:center;justify-content:space-between;gap:12px;padding:12px 14px;border-bottom:1px solid var(--fb-border-subtle, #eef0f4)}.fb-dialog__identity{min-width:0}.fb-dialog__type{display:block;font-size:10px;font-weight:700;letter-spacing:.06em;text-transform:uppercase;color:var(--fb-text-subtle, #98a2b3)}.fb-dialog__title{margin:1px 0 0;font-size:15px;font-weight:600;color:var(--fb-text, #1a1c23);overflow:hidden;text-overflow:ellipsis;white-space:nowrap}.fb-dialog__body{flex:1;min-height:0;overflow-y:auto;overscroll-behavior:contain}.fb-dialog__foot{display:flex;align-items:center;justify-content:space-between;gap:12px;padding:10px 14px;border-top:1px solid var(--fb-border-subtle, #eef0f4);background:var(--fb-surface-alt, #f7f8fa)}.fb-dialog__note{font-size:11px;color:var(--fb-text-muted, #6b7086)}@media(max-height:620px){:host{padding:10px}}\n"] }]
6056
6799
  }], ctorParameters: () => [], propDecorators: { selectedName: [{ type: i0.Input, args: [{ isSignal: true, alias: "selectedName", required: false }] }], closed: [{ type: i0.Output, args: ["closed"] }], removeRequested: [{ type: i0.Output, args: ["removeRequested"] }], duplicateRequested: [{ type: i0.Output, args: ["duplicateRequested"] }], panel: [{ type: i0.ViewChild, args: ['panel', { isSignal: true }] }] } });
6057
6800
 
6058
6801
  /**
@@ -6337,11 +7080,11 @@ class ProblemsPanelComponent {
6337
7080
  this.closed.emit();
6338
7081
  }
6339
7082
  static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.3.27", ngImport: i0, type: ProblemsPanelComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
6340
- static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "20.3.27", type: ProblemsPanelComponent, isStandalone: true, selector: "fb-problems-panel", outputs: { elementFocused: "elementFocused", closed: "closed" }, ngImport: i0, template: "<header class=\"fb-prob__header\">\n <h2 class=\"fb-prob__title\">\n Problemi\n @if (isRunning()) {\n <span class=\"fb-prob__running\">validazione in corso\u2026</span>\n }\n </h2>\n <button type=\"button\" class=\"fb-btn fb-btn--ghost fb-btn--icon\" aria-label=\"Chiudi\" (click)=\"close()\">\u00D7</button>\n</header>\n\n<nav class=\"fb-prob__filters\" aria-label=\"Filtra per gravita\u2019\">\n <button\n type=\"button\"\n class=\"fb-prob__filter\"\n [class.fb-prob__filter--active]=\"filter() === 'all'\"\n (click)=\"setFilter('all')\"\n >\n Tutti\n </button>\n <button\n type=\"button\"\n class=\"fb-prob__filter fb-prob__filter--error\"\n [class.fb-prob__filter--active]=\"filter() === 'Error'\"\n (click)=\"setFilter('Error')\"\n >\n Errori <span class=\"fb-prob__count\">{{ errorCount() }}</span>\n </button>\n <button\n type=\"button\"\n class=\"fb-prob__filter fb-prob__filter--warning\"\n [class.fb-prob__filter--active]=\"filter() === 'Warning'\"\n (click)=\"setFilter('Warning')\"\n >\n Avvisi <span class=\"fb-prob__count\">{{ warningCount() }}</span>\n </button>\n <button\n type=\"button\"\n class=\"fb-prob__filter\"\n [class.fb-prob__filter--active]=\"filter() === 'Info'\"\n (click)=\"setFilter('Info')\"\n >\n Note <span class=\"fb-prob__count\">{{ infoCount() }}</span>\n </button>\n</nav>\n\n<div class=\"fb-prob__body\">\n @if (lastError()) {\n <p class=\"fb-callout fb-callout--warn\">\n Non e\u2019 stato possibile validare: {{ lastError() }}\n </p>\n }\n\n @if (!hasResult() && !isRunning()) {\n <p class=\"fb-empty\">La validazione non e\u2019 ancora stata eseguita.</p>\n }\n\n @if (isClean()) {\n <p class=\"fb-empty\">\n Nessun rilievo. Attenzione: non e\u2019 una garanzia di correttezza \u2014 la validazione non verifica i percorsi\n di relazione nei campi, la sintassi delle formule, ne\u2019 action, form e parametri quando il catalogo\n corrispondente non e\u2019 popolato.\n </p>\n }\n\n <ul class=\"fb-prob__list\">\n <!-- `$index`: due rilievi possono avere codice, elemento e path identici (due condizioni\n incomplete nella stessa regola), e una chiave duplicata e' un errore di Angular. -->\n @for (issue of issues(); track $index) {\n <li\n class=\"fb-prob__item\"\n [class.fb-prob__item--error]=\"issue.severity === 'Error'\"\n [class.fb-prob__item--warning]=\"issue.severity === 'Warning'\"\n >\n <button type=\"button\" class=\"fb-prob__link\" (click)=\"focus(issue)\">\n <span class=\"fb-prob__message\">{{ issue.message }}</span>\n <span class=\"fb-prob__meta\">\n <code>{{ issue.code }}</code>\n @if (issue.elementName) {\n \u00B7 {{ issue.elementName }}\n }\n @if (issue.path) {\n \u00B7 {{ issue.path }}\n }\n </span>\n </button>\n </li>\n }\n </ul>\n\n @if (errorCount() > 0) {\n <p class=\"fb-prob__note\">\n Gli errori bloccano l\u2019<strong>attivazione</strong>, non il salvataggio: puoi interrompere il lavoro a\n met\u00E0 e riprenderlo.\n </p>\n }\n</div>\n", styles: [":host{display:flex;flex-direction:column;height:100%;min-height:0;background:var(--fb-surface, #fff);border-top:1px solid var(--fb-border, #d6dae1)}.fb-prob__header{display:flex;align-items:center;justify-content:space-between;padding:6px 12px;border-bottom:1px solid var(--fb-border-subtle, #e6e9ee)}.fb-prob__title{display:flex;align-items:baseline;gap:8px;margin:0;font-size:13px;font-weight:600;color:var(--fb-text, #1d2939)}.fb-prob__running{font-size:10px;font-weight:400;color:var(--fb-text-subtle, #98a2b3)}.fb-prob__filters{display:flex;gap:3px;padding:5px 12px;border-bottom:1px solid var(--fb-border-subtle, #e6e9ee)}.fb-prob__filter{display:inline-flex;align-items:center;gap:4px;padding:2px 8px;border:1px solid var(--fb-border-subtle, #e6e9ee);border-radius:12px;background:transparent;color:var(--fb-text-muted, #667085);font:inherit;font-size:11px;cursor:pointer}.fb-prob__filter--active{border-color:var(--fb-accent, #2f6feb);background:color-mix(in srgb,var(--fb-accent, #2f6feb) 10%,transparent);color:var(--fb-accent, #2f6feb);font-weight:600}.fb-prob__filter--error.fb-prob__filter--active{border-color:var(--fb-error, #c9372c);background:color-mix(in srgb,var(--fb-error, #c9372c) 10%,transparent);color:var(--fb-error, #c9372c)}.fb-prob__filter--warning.fb-prob__filter--active{border-color:var(--fb-warning, #b7791f);background:color-mix(in srgb,var(--fb-warning, #b7791f) 12%,transparent);color:var(--fb-warning, #b7791f)}.fb-prob__count{padding:0 4px;border-radius:6px;background:var(--fb-border, #d6dae1);font-size:9px;color:var(--fb-text, #1d2939)}.fb-prob__body{flex:1;min-height:0;overflow-y:auto;padding:8px 12px}.fb-prob__list{margin:0;padding:0;list-style:none}.fb-prob__item{border-left:3px solid var(--fb-text-subtle, #98a2b3);margin-bottom:3px;border-radius:3px;background:var(--fb-surface-alt, #f8f9fb)}.fb-prob__item--error{border-left-color:var(--fb-error, #c9372c)}.fb-prob__item--warning{border-left-color:var(--fb-warning, #b7791f)}.fb-prob__link{display:flex;flex-direction:column;width:100%;padding:5px 8px;border:0;background:transparent;color:var(--fb-text, #1d2939);font:inherit;text-align:left;cursor:pointer}.fb-prob__link:hover{background:color-mix(in srgb,var(--fb-accent, #2f6feb) 6%,transparent)}.fb-prob__message{font-size:12px;line-height:1.4}.fb-prob__meta{font-size:10px;color:var(--fb-text-muted, #667085)}.fb-prob__note{margin:8px 0 0;font-size:10px;color:var(--fb-text-subtle, #98a2b3)}\n"], changeDetection: i0.ChangeDetectionStrategy.OnPush });
7083
+ static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "20.3.27", type: ProblemsPanelComponent, isStandalone: true, selector: "fb-problems-panel", outputs: { elementFocused: "elementFocused", closed: "closed" }, ngImport: i0, template: "<header class=\"fb-prob__header\">\r\n <h2 class=\"fb-prob__title\">\r\n Problemi\r\n @if (isRunning()) {\r\n <span class=\"fb-prob__running\">validazione in corso\u2026</span>\r\n }\r\n </h2>\r\n <button type=\"button\" class=\"fb-btn fb-btn--ghost fb-btn--icon\" aria-label=\"Chiudi\" (click)=\"close()\">\u00D7</button>\r\n</header>\r\n\r\n<nav class=\"fb-prob__filters\" aria-label=\"Filtra per gravita\u2019\">\r\n <button\r\n type=\"button\"\r\n class=\"fb-prob__filter\"\r\n [class.fb-prob__filter--active]=\"filter() === 'all'\"\r\n (click)=\"setFilter('all')\"\r\n >\r\n Tutti\r\n </button>\r\n <button\r\n type=\"button\"\r\n class=\"fb-prob__filter fb-prob__filter--error\"\r\n [class.fb-prob__filter--active]=\"filter() === 'Error'\"\r\n (click)=\"setFilter('Error')\"\r\n >\r\n Errori <span class=\"fb-prob__count\">{{ errorCount() }}</span>\r\n </button>\r\n <button\r\n type=\"button\"\r\n class=\"fb-prob__filter fb-prob__filter--warning\"\r\n [class.fb-prob__filter--active]=\"filter() === 'Warning'\"\r\n (click)=\"setFilter('Warning')\"\r\n >\r\n Avvisi <span class=\"fb-prob__count\">{{ warningCount() }}</span>\r\n </button>\r\n <button\r\n type=\"button\"\r\n class=\"fb-prob__filter\"\r\n [class.fb-prob__filter--active]=\"filter() === 'Info'\"\r\n (click)=\"setFilter('Info')\"\r\n >\r\n Note <span class=\"fb-prob__count\">{{ infoCount() }}</span>\r\n </button>\r\n</nav>\r\n\r\n<div class=\"fb-prob__body\">\r\n @if (lastError()) {\r\n <p class=\"fb-callout fb-callout--warn\">\r\n Non e\u2019 stato possibile validare: {{ lastError() }}\r\n </p>\r\n }\r\n\r\n @if (!hasResult() && !isRunning()) {\r\n <p class=\"fb-empty\">La validazione non e\u2019 ancora stata eseguita.</p>\r\n }\r\n\r\n @if (isClean()) {\r\n <p class=\"fb-empty\">\r\n Nessun rilievo. Attenzione: non e\u2019 una garanzia di correttezza \u2014 la validazione non verifica i percorsi\r\n di relazione nei campi, la sintassi delle formule, ne\u2019 action, form e parametri quando il catalogo\r\n corrispondente non e\u2019 popolato.\r\n </p>\r\n }\r\n\r\n <ul class=\"fb-prob__list\">\r\n <!-- `$index`: due rilievi possono avere codice, elemento e path identici (due condizioni\r\n incomplete nella stessa regola), e una chiave duplicata e' un errore di Angular. -->\r\n @for (issue of issues(); track $index) {\r\n <li\r\n class=\"fb-prob__item\"\r\n [class.fb-prob__item--error]=\"issue.severity === 'Error'\"\r\n [class.fb-prob__item--warning]=\"issue.severity === 'Warning'\"\r\n >\r\n <button type=\"button\" class=\"fb-prob__link\" (click)=\"focus(issue)\">\r\n <span class=\"fb-prob__message\">{{ issue.message }}</span>\r\n <span class=\"fb-prob__meta\">\r\n <code>{{ issue.code }}</code>\r\n @if (issue.elementName) {\r\n \u00B7 {{ issue.elementName }}\r\n }\r\n @if (issue.path) {\r\n \u00B7 {{ issue.path }}\r\n }\r\n </span>\r\n </button>\r\n </li>\r\n }\r\n </ul>\r\n\r\n @if (errorCount() > 0) {\r\n <p class=\"fb-prob__note\">\r\n Gli errori bloccano l\u2019<strong>attivazione</strong>, non il salvataggio: puoi interrompere il lavoro a\r\n met\u00E0 e riprenderlo.\r\n </p>\r\n }\r\n</div>\r\n", styles: [":host{display:flex;flex-direction:column;height:100%;min-height:0;background:var(--fb-surface, #fff);border-top:1px solid var(--fb-border, #d6dae1)}.fb-prob__header{display:flex;align-items:center;justify-content:space-between;padding:6px 12px;border-bottom:1px solid var(--fb-border-subtle, #e6e9ee)}.fb-prob__title{display:flex;align-items:baseline;gap:8px;margin:0;font-size:13px;font-weight:600;color:var(--fb-text, #1d2939)}.fb-prob__running{font-size:10px;font-weight:400;color:var(--fb-text-subtle, #98a2b3)}.fb-prob__filters{display:flex;gap:3px;padding:5px 12px;border-bottom:1px solid var(--fb-border-subtle, #e6e9ee)}.fb-prob__filter{display:inline-flex;align-items:center;gap:4px;padding:2px 8px;border:1px solid var(--fb-border-subtle, #e6e9ee);border-radius:12px;background:transparent;color:var(--fb-text-muted, #667085);font:inherit;font-size:11px;cursor:pointer}.fb-prob__filter--active{border-color:var(--fb-accent, #2f6feb);background:color-mix(in srgb,var(--fb-accent, #2f6feb) 10%,transparent);color:var(--fb-accent, #2f6feb);font-weight:600}.fb-prob__filter--error.fb-prob__filter--active{border-color:var(--fb-error, #c9372c);background:color-mix(in srgb,var(--fb-error, #c9372c) 10%,transparent);color:var(--fb-error, #c9372c)}.fb-prob__filter--warning.fb-prob__filter--active{border-color:var(--fb-warning, #b7791f);background:color-mix(in srgb,var(--fb-warning, #b7791f) 12%,transparent);color:var(--fb-warning, #b7791f)}.fb-prob__count{padding:0 4px;border-radius:6px;background:var(--fb-border, #d6dae1);font-size:9px;color:var(--fb-text, #1d2939)}.fb-prob__body{flex:1;min-height:0;overflow-y:auto;padding:8px 12px}.fb-prob__list{margin:0;padding:0;list-style:none}.fb-prob__item{border-left:3px solid var(--fb-text-subtle, #98a2b3);margin-bottom:3px;border-radius:3px;background:var(--fb-surface-alt, #f8f9fb)}.fb-prob__item--error{border-left-color:var(--fb-error, #c9372c)}.fb-prob__item--warning{border-left-color:var(--fb-warning, #b7791f)}.fb-prob__link{display:flex;flex-direction:column;width:100%;padding:5px 8px;border:0;background:transparent;color:var(--fb-text, #1d2939);font:inherit;text-align:left;cursor:pointer}.fb-prob__link:hover{background:color-mix(in srgb,var(--fb-accent, #2f6feb) 6%,transparent)}.fb-prob__message{font-size:12px;line-height:1.4}.fb-prob__meta{font-size:10px;color:var(--fb-text-muted, #667085)}.fb-prob__note{margin:8px 0 0;font-size:10px;color:var(--fb-text-subtle, #98a2b3)}\n"], changeDetection: i0.ChangeDetectionStrategy.OnPush });
6341
7084
  }
6342
7085
  i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.27", ngImport: i0, type: ProblemsPanelComponent, decorators: [{
6343
7086
  type: Component,
6344
- args: [{ selector: 'fb-problems-panel', standalone: true, changeDetection: ChangeDetectionStrategy.OnPush, template: "<header class=\"fb-prob__header\">\n <h2 class=\"fb-prob__title\">\n Problemi\n @if (isRunning()) {\n <span class=\"fb-prob__running\">validazione in corso\u2026</span>\n }\n </h2>\n <button type=\"button\" class=\"fb-btn fb-btn--ghost fb-btn--icon\" aria-label=\"Chiudi\" (click)=\"close()\">\u00D7</button>\n</header>\n\n<nav class=\"fb-prob__filters\" aria-label=\"Filtra per gravita\u2019\">\n <button\n type=\"button\"\n class=\"fb-prob__filter\"\n [class.fb-prob__filter--active]=\"filter() === 'all'\"\n (click)=\"setFilter('all')\"\n >\n Tutti\n </button>\n <button\n type=\"button\"\n class=\"fb-prob__filter fb-prob__filter--error\"\n [class.fb-prob__filter--active]=\"filter() === 'Error'\"\n (click)=\"setFilter('Error')\"\n >\n Errori <span class=\"fb-prob__count\">{{ errorCount() }}</span>\n </button>\n <button\n type=\"button\"\n class=\"fb-prob__filter fb-prob__filter--warning\"\n [class.fb-prob__filter--active]=\"filter() === 'Warning'\"\n (click)=\"setFilter('Warning')\"\n >\n Avvisi <span class=\"fb-prob__count\">{{ warningCount() }}</span>\n </button>\n <button\n type=\"button\"\n class=\"fb-prob__filter\"\n [class.fb-prob__filter--active]=\"filter() === 'Info'\"\n (click)=\"setFilter('Info')\"\n >\n Note <span class=\"fb-prob__count\">{{ infoCount() }}</span>\n </button>\n</nav>\n\n<div class=\"fb-prob__body\">\n @if (lastError()) {\n <p class=\"fb-callout fb-callout--warn\">\n Non e\u2019 stato possibile validare: {{ lastError() }}\n </p>\n }\n\n @if (!hasResult() && !isRunning()) {\n <p class=\"fb-empty\">La validazione non e\u2019 ancora stata eseguita.</p>\n }\n\n @if (isClean()) {\n <p class=\"fb-empty\">\n Nessun rilievo. Attenzione: non e\u2019 una garanzia di correttezza \u2014 la validazione non verifica i percorsi\n di relazione nei campi, la sintassi delle formule, ne\u2019 action, form e parametri quando il catalogo\n corrispondente non e\u2019 popolato.\n </p>\n }\n\n <ul class=\"fb-prob__list\">\n <!-- `$index`: due rilievi possono avere codice, elemento e path identici (due condizioni\n incomplete nella stessa regola), e una chiave duplicata e' un errore di Angular. -->\n @for (issue of issues(); track $index) {\n <li\n class=\"fb-prob__item\"\n [class.fb-prob__item--error]=\"issue.severity === 'Error'\"\n [class.fb-prob__item--warning]=\"issue.severity === 'Warning'\"\n >\n <button type=\"button\" class=\"fb-prob__link\" (click)=\"focus(issue)\">\n <span class=\"fb-prob__message\">{{ issue.message }}</span>\n <span class=\"fb-prob__meta\">\n <code>{{ issue.code }}</code>\n @if (issue.elementName) {\n \u00B7 {{ issue.elementName }}\n }\n @if (issue.path) {\n \u00B7 {{ issue.path }}\n }\n </span>\n </button>\n </li>\n }\n </ul>\n\n @if (errorCount() > 0) {\n <p class=\"fb-prob__note\">\n Gli errori bloccano l\u2019<strong>attivazione</strong>, non il salvataggio: puoi interrompere il lavoro a\n met\u00E0 e riprenderlo.\n </p>\n }\n</div>\n", styles: [":host{display:flex;flex-direction:column;height:100%;min-height:0;background:var(--fb-surface, #fff);border-top:1px solid var(--fb-border, #d6dae1)}.fb-prob__header{display:flex;align-items:center;justify-content:space-between;padding:6px 12px;border-bottom:1px solid var(--fb-border-subtle, #e6e9ee)}.fb-prob__title{display:flex;align-items:baseline;gap:8px;margin:0;font-size:13px;font-weight:600;color:var(--fb-text, #1d2939)}.fb-prob__running{font-size:10px;font-weight:400;color:var(--fb-text-subtle, #98a2b3)}.fb-prob__filters{display:flex;gap:3px;padding:5px 12px;border-bottom:1px solid var(--fb-border-subtle, #e6e9ee)}.fb-prob__filter{display:inline-flex;align-items:center;gap:4px;padding:2px 8px;border:1px solid var(--fb-border-subtle, #e6e9ee);border-radius:12px;background:transparent;color:var(--fb-text-muted, #667085);font:inherit;font-size:11px;cursor:pointer}.fb-prob__filter--active{border-color:var(--fb-accent, #2f6feb);background:color-mix(in srgb,var(--fb-accent, #2f6feb) 10%,transparent);color:var(--fb-accent, #2f6feb);font-weight:600}.fb-prob__filter--error.fb-prob__filter--active{border-color:var(--fb-error, #c9372c);background:color-mix(in srgb,var(--fb-error, #c9372c) 10%,transparent);color:var(--fb-error, #c9372c)}.fb-prob__filter--warning.fb-prob__filter--active{border-color:var(--fb-warning, #b7791f);background:color-mix(in srgb,var(--fb-warning, #b7791f) 12%,transparent);color:var(--fb-warning, #b7791f)}.fb-prob__count{padding:0 4px;border-radius:6px;background:var(--fb-border, #d6dae1);font-size:9px;color:var(--fb-text, #1d2939)}.fb-prob__body{flex:1;min-height:0;overflow-y:auto;padding:8px 12px}.fb-prob__list{margin:0;padding:0;list-style:none}.fb-prob__item{border-left:3px solid var(--fb-text-subtle, #98a2b3);margin-bottom:3px;border-radius:3px;background:var(--fb-surface-alt, #f8f9fb)}.fb-prob__item--error{border-left-color:var(--fb-error, #c9372c)}.fb-prob__item--warning{border-left-color:var(--fb-warning, #b7791f)}.fb-prob__link{display:flex;flex-direction:column;width:100%;padding:5px 8px;border:0;background:transparent;color:var(--fb-text, #1d2939);font:inherit;text-align:left;cursor:pointer}.fb-prob__link:hover{background:color-mix(in srgb,var(--fb-accent, #2f6feb) 6%,transparent)}.fb-prob__message{font-size:12px;line-height:1.4}.fb-prob__meta{font-size:10px;color:var(--fb-text-muted, #667085)}.fb-prob__note{margin:8px 0 0;font-size:10px;color:var(--fb-text-subtle, #98a2b3)}\n"] }]
7087
+ args: [{ selector: 'fb-problems-panel', standalone: true, changeDetection: ChangeDetectionStrategy.OnPush, template: "<header class=\"fb-prob__header\">\r\n <h2 class=\"fb-prob__title\">\r\n Problemi\r\n @if (isRunning()) {\r\n <span class=\"fb-prob__running\">validazione in corso\u2026</span>\r\n }\r\n </h2>\r\n <button type=\"button\" class=\"fb-btn fb-btn--ghost fb-btn--icon\" aria-label=\"Chiudi\" (click)=\"close()\">\u00D7</button>\r\n</header>\r\n\r\n<nav class=\"fb-prob__filters\" aria-label=\"Filtra per gravita\u2019\">\r\n <button\r\n type=\"button\"\r\n class=\"fb-prob__filter\"\r\n [class.fb-prob__filter--active]=\"filter() === 'all'\"\r\n (click)=\"setFilter('all')\"\r\n >\r\n Tutti\r\n </button>\r\n <button\r\n type=\"button\"\r\n class=\"fb-prob__filter fb-prob__filter--error\"\r\n [class.fb-prob__filter--active]=\"filter() === 'Error'\"\r\n (click)=\"setFilter('Error')\"\r\n >\r\n Errori <span class=\"fb-prob__count\">{{ errorCount() }}</span>\r\n </button>\r\n <button\r\n type=\"button\"\r\n class=\"fb-prob__filter fb-prob__filter--warning\"\r\n [class.fb-prob__filter--active]=\"filter() === 'Warning'\"\r\n (click)=\"setFilter('Warning')\"\r\n >\r\n Avvisi <span class=\"fb-prob__count\">{{ warningCount() }}</span>\r\n </button>\r\n <button\r\n type=\"button\"\r\n class=\"fb-prob__filter\"\r\n [class.fb-prob__filter--active]=\"filter() === 'Info'\"\r\n (click)=\"setFilter('Info')\"\r\n >\r\n Note <span class=\"fb-prob__count\">{{ infoCount() }}</span>\r\n </button>\r\n</nav>\r\n\r\n<div class=\"fb-prob__body\">\r\n @if (lastError()) {\r\n <p class=\"fb-callout fb-callout--warn\">\r\n Non e\u2019 stato possibile validare: {{ lastError() }}\r\n </p>\r\n }\r\n\r\n @if (!hasResult() && !isRunning()) {\r\n <p class=\"fb-empty\">La validazione non e\u2019 ancora stata eseguita.</p>\r\n }\r\n\r\n @if (isClean()) {\r\n <p class=\"fb-empty\">\r\n Nessun rilievo. Attenzione: non e\u2019 una garanzia di correttezza \u2014 la validazione non verifica i percorsi\r\n di relazione nei campi, la sintassi delle formule, ne\u2019 action, form e parametri quando il catalogo\r\n corrispondente non e\u2019 popolato.\r\n </p>\r\n }\r\n\r\n <ul class=\"fb-prob__list\">\r\n <!-- `$index`: due rilievi possono avere codice, elemento e path identici (due condizioni\r\n incomplete nella stessa regola), e una chiave duplicata e' un errore di Angular. -->\r\n @for (issue of issues(); track $index) {\r\n <li\r\n class=\"fb-prob__item\"\r\n [class.fb-prob__item--error]=\"issue.severity === 'Error'\"\r\n [class.fb-prob__item--warning]=\"issue.severity === 'Warning'\"\r\n >\r\n <button type=\"button\" class=\"fb-prob__link\" (click)=\"focus(issue)\">\r\n <span class=\"fb-prob__message\">{{ issue.message }}</span>\r\n <span class=\"fb-prob__meta\">\r\n <code>{{ issue.code }}</code>\r\n @if (issue.elementName) {\r\n \u00B7 {{ issue.elementName }}\r\n }\r\n @if (issue.path) {\r\n \u00B7 {{ issue.path }}\r\n }\r\n </span>\r\n </button>\r\n </li>\r\n }\r\n </ul>\r\n\r\n @if (errorCount() > 0) {\r\n <p class=\"fb-prob__note\">\r\n Gli errori bloccano l\u2019<strong>attivazione</strong>, non il salvataggio: puoi interrompere il lavoro a\r\n met\u00E0 e riprenderlo.\r\n </p>\r\n }\r\n</div>\r\n", styles: [":host{display:flex;flex-direction:column;height:100%;min-height:0;background:var(--fb-surface, #fff);border-top:1px solid var(--fb-border, #d6dae1)}.fb-prob__header{display:flex;align-items:center;justify-content:space-between;padding:6px 12px;border-bottom:1px solid var(--fb-border-subtle, #e6e9ee)}.fb-prob__title{display:flex;align-items:baseline;gap:8px;margin:0;font-size:13px;font-weight:600;color:var(--fb-text, #1d2939)}.fb-prob__running{font-size:10px;font-weight:400;color:var(--fb-text-subtle, #98a2b3)}.fb-prob__filters{display:flex;gap:3px;padding:5px 12px;border-bottom:1px solid var(--fb-border-subtle, #e6e9ee)}.fb-prob__filter{display:inline-flex;align-items:center;gap:4px;padding:2px 8px;border:1px solid var(--fb-border-subtle, #e6e9ee);border-radius:12px;background:transparent;color:var(--fb-text-muted, #667085);font:inherit;font-size:11px;cursor:pointer}.fb-prob__filter--active{border-color:var(--fb-accent, #2f6feb);background:color-mix(in srgb,var(--fb-accent, #2f6feb) 10%,transparent);color:var(--fb-accent, #2f6feb);font-weight:600}.fb-prob__filter--error.fb-prob__filter--active{border-color:var(--fb-error, #c9372c);background:color-mix(in srgb,var(--fb-error, #c9372c) 10%,transparent);color:var(--fb-error, #c9372c)}.fb-prob__filter--warning.fb-prob__filter--active{border-color:var(--fb-warning, #b7791f);background:color-mix(in srgb,var(--fb-warning, #b7791f) 12%,transparent);color:var(--fb-warning, #b7791f)}.fb-prob__count{padding:0 4px;border-radius:6px;background:var(--fb-border, #d6dae1);font-size:9px;color:var(--fb-text, #1d2939)}.fb-prob__body{flex:1;min-height:0;overflow-y:auto;padding:8px 12px}.fb-prob__list{margin:0;padding:0;list-style:none}.fb-prob__item{border-left:3px solid var(--fb-text-subtle, #98a2b3);margin-bottom:3px;border-radius:3px;background:var(--fb-surface-alt, #f8f9fb)}.fb-prob__item--error{border-left-color:var(--fb-error, #c9372c)}.fb-prob__item--warning{border-left-color:var(--fb-warning, #b7791f)}.fb-prob__link{display:flex;flex-direction:column;width:100%;padding:5px 8px;border:0;background:transparent;color:var(--fb-text, #1d2939);font:inherit;text-align:left;cursor:pointer}.fb-prob__link:hover{background:color-mix(in srgb,var(--fb-accent, #2f6feb) 6%,transparent)}.fb-prob__message{font-size:12px;line-height:1.4}.fb-prob__meta{font-size:10px;color:var(--fb-text-muted, #667085)}.fb-prob__note{margin:8px 0 0;font-size:10px;color:var(--fb-text-subtle, #98a2b3)}\n"] }]
6345
7088
  }], propDecorators: { elementFocused: [{ type: i0.Output, args: ["elementFocused"] }], closed: [{ type: i0.Output, args: ["closed"] }] } });
6346
7089
 
6347
7090
  /**
@@ -6457,17 +7200,17 @@ class VersionPanelComponent {
6457
7200
  this.closed.emit();
6458
7201
  }
6459
7202
  static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.3.27", ngImport: i0, type: VersionPanelComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
6460
- static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "20.3.27", type: VersionPanelComponent, isStandalone: true, selector: "fb-version-panel", outputs: { closed: "closed", versionOpened: "versionOpened", notice: "notice" }, ngImport: i0, template: "<header class=\"fb-ver__header\">\n <h2 class=\"fb-ver__title\">Versioni</h2>\n <button type=\"button\" class=\"fb-btn fb-btn--ghost fb-btn--icon\" aria-label=\"Chiudi\" (click)=\"close()\">\u00D7</button>\n</header>\n\n<div class=\"fb-ver__body\">\n <div class=\"fb-field__row\">\n <button type=\"button\" class=\"fb-btn\" [disabled]=\"isBusy()\" (click)=\"createNewVersion()\">\n Nuova versione dalla corrente\n </button>\n @if (hasActiveVersion()) {\n <button type=\"button\" class=\"fb-btn fb-btn--danger\" [disabled]=\"isBusy()\" (click)=\"deactivate()\">\n Disattiva\n </button>\n }\n </div>\n\n <ul class=\"fb-ver__list\">\n @for (version of versions(); track version.version) {\n <li class=\"fb-ver__item\" [class.fb-ver__item--current]=\"isCurrent(version)\">\n <div class=\"fb-ver__row\">\n <button type=\"button\" class=\"fb-ver__open\" (click)=\"open(version)\">\n <span class=\"fb-ver__number\">v{{ version.version }}</span>\n <span\n class=\"fb-ver__status\"\n [class.fb-ver__status--active]=\"version.isActive\"\n [class.fb-ver__status--invalid]=\"version.status === 'InvalidDraft'\"\n >\n {{ statusLabel(version) }}\n </span>\n @if (isCurrent(version)) {\n <span class=\"fb-ver__badge\">aperta</span>\n }\n </button>\n </div>\n\n <p class=\"fb-ver__meta\">\n @if (version.updatedAt) {\n aggiornata {{ version.updatedAt }}\n }\n @if (version.updatedBy) {\n da {{ version.updatedBy }}\n }\n </p>\n\n <div class=\"fb-ver__actions\">\n @if (version.isEditable) {\n <span class=\"fb-ver__hint\">Modificabile</span>\n } @else {\n <!-- Salvare su una versione Active o Obsolete verrebbe rifiutato con NotEditable. -->\n <span class=\"fb-ver__hint\">Sola lettura: per modificarla, creane una nuova</span>\n }\n\n @if (!version.isActive && isCurrent(version)) {\n <button\n type=\"button\"\n class=\"fb-btn fb-btn--primary fb-btn--icon\"\n [disabled]=\"isBusy() || !canActivate()\"\n [title]=\"canActivate() ? 'Attiva questa versione' : 'Ci sono errori da correggere prima di attivare'\"\n (click)=\"activate(version)\"\n >\n Attiva\n </button>\n }\n\n @if (!version.isActive) {\n @if (pendingDelete() === version.version) {\n <span class=\"fb-ver__confirm\">\n Le esecuzioni sospese nate su questo numero riprenderebbero su una definizione diversa.\n <button type=\"button\" class=\"fb-btn fb-btn--danger fb-btn--icon\" (click)=\"confirmDelete(version.version)\">\n Elimina comunque\n </button>\n <button type=\"button\" class=\"fb-btn fb-btn--ghost fb-btn--icon\" (click)=\"cancelDelete()\">\n Annulla\n </button>\n </span>\n } @else {\n <button\n type=\"button\"\n class=\"fb-btn fb-btn--ghost fb-btn--icon\"\n [disabled]=\"isBusy()\"\n (click)=\"requestDelete(version)\"\n >\n Elimina\n </button>\n }\n }\n </div>\n </li>\n } @empty {\n <li class=\"fb-empty\">Nessuna versione: il flow non e\u2019 ancora stato salvato.</li>\n }\n </ul>\n\n <p class=\"fb-ver__note\">\n Una sola versione e\u2019 attiva: attivarne una rende superata la precedente, senza doverla disattivare prima.\n Le versioni superate costano poco \u2014 lo stato \u00ABSuperata\u00BB esiste per questo.\n </p>\n</div>\n", styles: [":host{display:flex;flex-direction:column;height:100%;min-height:0;background:var(--fb-surface, #fff)}.fb-ver__header{display:flex;align-items:center;justify-content:space-between;padding:10px 12px;border-bottom:1px solid var(--fb-border-subtle, #e6e9ee)}.fb-ver__title{margin:0;font-size:14px;font-weight:600;color:var(--fb-text, #1d2939)}.fb-ver__body{flex:1;min-height:0;overflow-y:auto;padding:10px 12px}.fb-ver__list{margin:12px 0 0;padding:0;list-style:none}.fb-ver__item{margin-bottom:6px;padding:8px;border:1px solid var(--fb-border-subtle, #e6e9ee);border-radius:6px;background:var(--fb-surface-alt, #f8f9fb)}.fb-ver__item--current{border-color:var(--fb-accent, #2f6feb);background:color-mix(in srgb,var(--fb-accent, #2f6feb) 5%,transparent)}.fb-ver__open{display:flex;align-items:center;gap:6px;width:100%;padding:0;border:0;background:transparent;color:var(--fb-text, #1d2939);font:inherit;text-align:left;cursor:pointer}.fb-ver__number{font-family:ui-monospace,SFMono-Regular,Menlo,monospace;font-size:13px;font-weight:700}.fb-ver__status{padding:1px 6px;border-radius:8px;background:var(--fb-border, #d6dae1);font-size:10px;font-weight:600;color:var(--fb-text, #1d2939)}.fb-ver__status--active{background:color-mix(in srgb,var(--fb-success, #3f8f5f) 18%,transparent);color:var(--fb-success, #3f8f5f)}.fb-ver__status--invalid{background:color-mix(in srgb,var(--fb-warning, #b7791f) 16%,transparent);color:var(--fb-warning, #b7791f)}.fb-ver__badge{font-size:9px;color:var(--fb-accent, #2f6feb);text-transform:uppercase;letter-spacing:.05em}.fb-ver__meta{margin:3px 0 0;font-size:10px;color:var(--fb-text-muted, #667085)}.fb-ver__actions{display:flex;flex-wrap:wrap;align-items:center;gap:6px;margin-top:6px}.fb-ver__hint{font-size:10px;color:var(--fb-text-subtle, #98a2b3)}.fb-ver__confirm{display:flex;flex-wrap:wrap;align-items:center;gap:4px;padding:5px 7px;border-radius:5px;background:color-mix(in srgb,var(--fb-error, #c9372c) 8%,transparent);font-size:10px;color:var(--fb-error, #c9372c)}.fb-ver__note{margin:12px 0 0;font-size:10px;line-height:1.45;color:var(--fb-text-subtle, #98a2b3)}\n"], changeDetection: i0.ChangeDetectionStrategy.OnPush });
7203
+ static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "20.3.27", type: VersionPanelComponent, isStandalone: true, selector: "fb-version-panel", outputs: { closed: "closed", versionOpened: "versionOpened", notice: "notice" }, ngImport: i0, template: "<header class=\"fb-ver__header\">\r\n <h2 class=\"fb-ver__title\">Versioni</h2>\r\n <button type=\"button\" class=\"fb-btn fb-btn--ghost fb-btn--icon\" aria-label=\"Chiudi\" (click)=\"close()\">\u00D7</button>\r\n</header>\r\n\r\n<div class=\"fb-ver__body\">\r\n <div class=\"fb-field__row\">\r\n <button type=\"button\" class=\"fb-btn\" [disabled]=\"isBusy()\" (click)=\"createNewVersion()\">\r\n Nuova versione dalla corrente\r\n </button>\r\n @if (hasActiveVersion()) {\r\n <button type=\"button\" class=\"fb-btn fb-btn--danger\" [disabled]=\"isBusy()\" (click)=\"deactivate()\">\r\n Disattiva\r\n </button>\r\n }\r\n </div>\r\n\r\n <ul class=\"fb-ver__list\">\r\n @for (version of versions(); track version.version) {\r\n <li class=\"fb-ver__item\" [class.fb-ver__item--current]=\"isCurrent(version)\">\r\n <div class=\"fb-ver__row\">\r\n <button type=\"button\" class=\"fb-ver__open\" (click)=\"open(version)\">\r\n <span class=\"fb-ver__number\">v{{ version.version }}</span>\r\n <span\r\n class=\"fb-ver__status\"\r\n [class.fb-ver__status--active]=\"version.isActive\"\r\n [class.fb-ver__status--invalid]=\"version.status === 'InvalidDraft'\"\r\n >\r\n {{ statusLabel(version) }}\r\n </span>\r\n @if (isCurrent(version)) {\r\n <span class=\"fb-ver__badge\">aperta</span>\r\n }\r\n </button>\r\n </div>\r\n\r\n <p class=\"fb-ver__meta\">\r\n @if (version.updatedAt) {\r\n aggiornata {{ version.updatedAt }}\r\n }\r\n @if (version.updatedBy) {\r\n da {{ version.updatedBy }}\r\n }\r\n </p>\r\n\r\n <div class=\"fb-ver__actions\">\r\n @if (version.isEditable) {\r\n <span class=\"fb-ver__hint\">Modificabile</span>\r\n } @else {\r\n <!-- Salvare su una versione Active o Obsolete verrebbe rifiutato con NotEditable. -->\r\n <span class=\"fb-ver__hint\">Sola lettura: per modificarla, creane una nuova</span>\r\n }\r\n\r\n @if (!version.isActive && isCurrent(version)) {\r\n <button\r\n type=\"button\"\r\n class=\"fb-btn fb-btn--primary fb-btn--icon\"\r\n [disabled]=\"isBusy() || !canActivate()\"\r\n [title]=\"canActivate() ? 'Attiva questa versione' : 'Ci sono errori da correggere prima di attivare'\"\r\n (click)=\"activate(version)\"\r\n >\r\n Attiva\r\n </button>\r\n }\r\n\r\n @if (!version.isActive) {\r\n @if (pendingDelete() === version.version) {\r\n <span class=\"fb-ver__confirm\">\r\n Le esecuzioni sospese nate su questo numero riprenderebbero su una definizione diversa.\r\n <button type=\"button\" class=\"fb-btn fb-btn--danger fb-btn--icon\" (click)=\"confirmDelete(version.version)\">\r\n Elimina comunque\r\n </button>\r\n <button type=\"button\" class=\"fb-btn fb-btn--ghost fb-btn--icon\" (click)=\"cancelDelete()\">\r\n Annulla\r\n </button>\r\n </span>\r\n } @else {\r\n <button\r\n type=\"button\"\r\n class=\"fb-btn fb-btn--ghost fb-btn--icon\"\r\n [disabled]=\"isBusy()\"\r\n (click)=\"requestDelete(version)\"\r\n >\r\n Elimina\r\n </button>\r\n }\r\n }\r\n </div>\r\n </li>\r\n } @empty {\r\n <li class=\"fb-empty\">Nessuna versione: il flow non e\u2019 ancora stato salvato.</li>\r\n }\r\n </ul>\r\n\r\n <p class=\"fb-ver__note\">\r\n Una sola versione e\u2019 attiva: attivarne una rende superata la precedente, senza doverla disattivare prima.\r\n Le versioni superate costano poco \u2014 lo stato \u00ABSuperata\u00BB esiste per questo.\r\n </p>\r\n</div>\r\n", styles: [":host{display:flex;flex-direction:column;height:100%;min-height:0;background:var(--fb-surface, #fff)}.fb-ver__header{display:flex;align-items:center;justify-content:space-between;padding:10px 12px;border-bottom:1px solid var(--fb-border-subtle, #e6e9ee)}.fb-ver__title{margin:0;font-size:14px;font-weight:600;color:var(--fb-text, #1d2939)}.fb-ver__body{flex:1;min-height:0;overflow-y:auto;padding:10px 12px}.fb-ver__list{margin:12px 0 0;padding:0;list-style:none}.fb-ver__item{margin-bottom:6px;padding:8px;border:1px solid var(--fb-border-subtle, #e6e9ee);border-radius:6px;background:var(--fb-surface-alt, #f8f9fb)}.fb-ver__item--current{border-color:var(--fb-accent, #2f6feb);background:color-mix(in srgb,var(--fb-accent, #2f6feb) 5%,transparent)}.fb-ver__open{display:flex;align-items:center;gap:6px;width:100%;padding:0;border:0;background:transparent;color:var(--fb-text, #1d2939);font:inherit;text-align:left;cursor:pointer}.fb-ver__number{font-family:ui-monospace,SFMono-Regular,Menlo,monospace;font-size:13px;font-weight:700}.fb-ver__status{padding:1px 6px;border-radius:8px;background:var(--fb-border, #d6dae1);font-size:10px;font-weight:600;color:var(--fb-text, #1d2939)}.fb-ver__status--active{background:color-mix(in srgb,var(--fb-success, #3f8f5f) 18%,transparent);color:var(--fb-success, #3f8f5f)}.fb-ver__status--invalid{background:color-mix(in srgb,var(--fb-warning, #b7791f) 16%,transparent);color:var(--fb-warning, #b7791f)}.fb-ver__badge{font-size:9px;color:var(--fb-accent, #2f6feb);text-transform:uppercase;letter-spacing:.05em}.fb-ver__meta{margin:3px 0 0;font-size:10px;color:var(--fb-text-muted, #667085)}.fb-ver__actions{display:flex;flex-wrap:wrap;align-items:center;gap:6px;margin-top:6px}.fb-ver__hint{font-size:10px;color:var(--fb-text-subtle, #98a2b3)}.fb-ver__confirm{display:flex;flex-wrap:wrap;align-items:center;gap:4px;padding:5px 7px;border-radius:5px;background:color-mix(in srgb,var(--fb-error, #c9372c) 8%,transparent);font-size:10px;color:var(--fb-error, #c9372c)}.fb-ver__note{margin:12px 0 0;font-size:10px;line-height:1.45;color:var(--fb-text-subtle, #98a2b3)}\n"], changeDetection: i0.ChangeDetectionStrategy.OnPush });
6461
7204
  }
6462
7205
  i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.27", ngImport: i0, type: VersionPanelComponent, decorators: [{
6463
7206
  type: Component,
6464
- args: [{ selector: 'fb-version-panel', standalone: true, changeDetection: ChangeDetectionStrategy.OnPush, template: "<header class=\"fb-ver__header\">\n <h2 class=\"fb-ver__title\">Versioni</h2>\n <button type=\"button\" class=\"fb-btn fb-btn--ghost fb-btn--icon\" aria-label=\"Chiudi\" (click)=\"close()\">\u00D7</button>\n</header>\n\n<div class=\"fb-ver__body\">\n <div class=\"fb-field__row\">\n <button type=\"button\" class=\"fb-btn\" [disabled]=\"isBusy()\" (click)=\"createNewVersion()\">\n Nuova versione dalla corrente\n </button>\n @if (hasActiveVersion()) {\n <button type=\"button\" class=\"fb-btn fb-btn--danger\" [disabled]=\"isBusy()\" (click)=\"deactivate()\">\n Disattiva\n </button>\n }\n </div>\n\n <ul class=\"fb-ver__list\">\n @for (version of versions(); track version.version) {\n <li class=\"fb-ver__item\" [class.fb-ver__item--current]=\"isCurrent(version)\">\n <div class=\"fb-ver__row\">\n <button type=\"button\" class=\"fb-ver__open\" (click)=\"open(version)\">\n <span class=\"fb-ver__number\">v{{ version.version }}</span>\n <span\n class=\"fb-ver__status\"\n [class.fb-ver__status--active]=\"version.isActive\"\n [class.fb-ver__status--invalid]=\"version.status === 'InvalidDraft'\"\n >\n {{ statusLabel(version) }}\n </span>\n @if (isCurrent(version)) {\n <span class=\"fb-ver__badge\">aperta</span>\n }\n </button>\n </div>\n\n <p class=\"fb-ver__meta\">\n @if (version.updatedAt) {\n aggiornata {{ version.updatedAt }}\n }\n @if (version.updatedBy) {\n da {{ version.updatedBy }}\n }\n </p>\n\n <div class=\"fb-ver__actions\">\n @if (version.isEditable) {\n <span class=\"fb-ver__hint\">Modificabile</span>\n } @else {\n <!-- Salvare su una versione Active o Obsolete verrebbe rifiutato con NotEditable. -->\n <span class=\"fb-ver__hint\">Sola lettura: per modificarla, creane una nuova</span>\n }\n\n @if (!version.isActive && isCurrent(version)) {\n <button\n type=\"button\"\n class=\"fb-btn fb-btn--primary fb-btn--icon\"\n [disabled]=\"isBusy() || !canActivate()\"\n [title]=\"canActivate() ? 'Attiva questa versione' : 'Ci sono errori da correggere prima di attivare'\"\n (click)=\"activate(version)\"\n >\n Attiva\n </button>\n }\n\n @if (!version.isActive) {\n @if (pendingDelete() === version.version) {\n <span class=\"fb-ver__confirm\">\n Le esecuzioni sospese nate su questo numero riprenderebbero su una definizione diversa.\n <button type=\"button\" class=\"fb-btn fb-btn--danger fb-btn--icon\" (click)=\"confirmDelete(version.version)\">\n Elimina comunque\n </button>\n <button type=\"button\" class=\"fb-btn fb-btn--ghost fb-btn--icon\" (click)=\"cancelDelete()\">\n Annulla\n </button>\n </span>\n } @else {\n <button\n type=\"button\"\n class=\"fb-btn fb-btn--ghost fb-btn--icon\"\n [disabled]=\"isBusy()\"\n (click)=\"requestDelete(version)\"\n >\n Elimina\n </button>\n }\n }\n </div>\n </li>\n } @empty {\n <li class=\"fb-empty\">Nessuna versione: il flow non e\u2019 ancora stato salvato.</li>\n }\n </ul>\n\n <p class=\"fb-ver__note\">\n Una sola versione e\u2019 attiva: attivarne una rende superata la precedente, senza doverla disattivare prima.\n Le versioni superate costano poco \u2014 lo stato \u00ABSuperata\u00BB esiste per questo.\n </p>\n</div>\n", styles: [":host{display:flex;flex-direction:column;height:100%;min-height:0;background:var(--fb-surface, #fff)}.fb-ver__header{display:flex;align-items:center;justify-content:space-between;padding:10px 12px;border-bottom:1px solid var(--fb-border-subtle, #e6e9ee)}.fb-ver__title{margin:0;font-size:14px;font-weight:600;color:var(--fb-text, #1d2939)}.fb-ver__body{flex:1;min-height:0;overflow-y:auto;padding:10px 12px}.fb-ver__list{margin:12px 0 0;padding:0;list-style:none}.fb-ver__item{margin-bottom:6px;padding:8px;border:1px solid var(--fb-border-subtle, #e6e9ee);border-radius:6px;background:var(--fb-surface-alt, #f8f9fb)}.fb-ver__item--current{border-color:var(--fb-accent, #2f6feb);background:color-mix(in srgb,var(--fb-accent, #2f6feb) 5%,transparent)}.fb-ver__open{display:flex;align-items:center;gap:6px;width:100%;padding:0;border:0;background:transparent;color:var(--fb-text, #1d2939);font:inherit;text-align:left;cursor:pointer}.fb-ver__number{font-family:ui-monospace,SFMono-Regular,Menlo,monospace;font-size:13px;font-weight:700}.fb-ver__status{padding:1px 6px;border-radius:8px;background:var(--fb-border, #d6dae1);font-size:10px;font-weight:600;color:var(--fb-text, #1d2939)}.fb-ver__status--active{background:color-mix(in srgb,var(--fb-success, #3f8f5f) 18%,transparent);color:var(--fb-success, #3f8f5f)}.fb-ver__status--invalid{background:color-mix(in srgb,var(--fb-warning, #b7791f) 16%,transparent);color:var(--fb-warning, #b7791f)}.fb-ver__badge{font-size:9px;color:var(--fb-accent, #2f6feb);text-transform:uppercase;letter-spacing:.05em}.fb-ver__meta{margin:3px 0 0;font-size:10px;color:var(--fb-text-muted, #667085)}.fb-ver__actions{display:flex;flex-wrap:wrap;align-items:center;gap:6px;margin-top:6px}.fb-ver__hint{font-size:10px;color:var(--fb-text-subtle, #98a2b3)}.fb-ver__confirm{display:flex;flex-wrap:wrap;align-items:center;gap:4px;padding:5px 7px;border-radius:5px;background:color-mix(in srgb,var(--fb-error, #c9372c) 8%,transparent);font-size:10px;color:var(--fb-error, #c9372c)}.fb-ver__note{margin:12px 0 0;font-size:10px;line-height:1.45;color:var(--fb-text-subtle, #98a2b3)}\n"] }]
7207
+ args: [{ selector: 'fb-version-panel', standalone: true, changeDetection: ChangeDetectionStrategy.OnPush, template: "<header class=\"fb-ver__header\">\r\n <h2 class=\"fb-ver__title\">Versioni</h2>\r\n <button type=\"button\" class=\"fb-btn fb-btn--ghost fb-btn--icon\" aria-label=\"Chiudi\" (click)=\"close()\">\u00D7</button>\r\n</header>\r\n\r\n<div class=\"fb-ver__body\">\r\n <div class=\"fb-field__row\">\r\n <button type=\"button\" class=\"fb-btn\" [disabled]=\"isBusy()\" (click)=\"createNewVersion()\">\r\n Nuova versione dalla corrente\r\n </button>\r\n @if (hasActiveVersion()) {\r\n <button type=\"button\" class=\"fb-btn fb-btn--danger\" [disabled]=\"isBusy()\" (click)=\"deactivate()\">\r\n Disattiva\r\n </button>\r\n }\r\n </div>\r\n\r\n <ul class=\"fb-ver__list\">\r\n @for (version of versions(); track version.version) {\r\n <li class=\"fb-ver__item\" [class.fb-ver__item--current]=\"isCurrent(version)\">\r\n <div class=\"fb-ver__row\">\r\n <button type=\"button\" class=\"fb-ver__open\" (click)=\"open(version)\">\r\n <span class=\"fb-ver__number\">v{{ version.version }}</span>\r\n <span\r\n class=\"fb-ver__status\"\r\n [class.fb-ver__status--active]=\"version.isActive\"\r\n [class.fb-ver__status--invalid]=\"version.status === 'InvalidDraft'\"\r\n >\r\n {{ statusLabel(version) }}\r\n </span>\r\n @if (isCurrent(version)) {\r\n <span class=\"fb-ver__badge\">aperta</span>\r\n }\r\n </button>\r\n </div>\r\n\r\n <p class=\"fb-ver__meta\">\r\n @if (version.updatedAt) {\r\n aggiornata {{ version.updatedAt }}\r\n }\r\n @if (version.updatedBy) {\r\n da {{ version.updatedBy }}\r\n }\r\n </p>\r\n\r\n <div class=\"fb-ver__actions\">\r\n @if (version.isEditable) {\r\n <span class=\"fb-ver__hint\">Modificabile</span>\r\n } @else {\r\n <!-- Salvare su una versione Active o Obsolete verrebbe rifiutato con NotEditable. -->\r\n <span class=\"fb-ver__hint\">Sola lettura: per modificarla, creane una nuova</span>\r\n }\r\n\r\n @if (!version.isActive && isCurrent(version)) {\r\n <button\r\n type=\"button\"\r\n class=\"fb-btn fb-btn--primary fb-btn--icon\"\r\n [disabled]=\"isBusy() || !canActivate()\"\r\n [title]=\"canActivate() ? 'Attiva questa versione' : 'Ci sono errori da correggere prima di attivare'\"\r\n (click)=\"activate(version)\"\r\n >\r\n Attiva\r\n </button>\r\n }\r\n\r\n @if (!version.isActive) {\r\n @if (pendingDelete() === version.version) {\r\n <span class=\"fb-ver__confirm\">\r\n Le esecuzioni sospese nate su questo numero riprenderebbero su una definizione diversa.\r\n <button type=\"button\" class=\"fb-btn fb-btn--danger fb-btn--icon\" (click)=\"confirmDelete(version.version)\">\r\n Elimina comunque\r\n </button>\r\n <button type=\"button\" class=\"fb-btn fb-btn--ghost fb-btn--icon\" (click)=\"cancelDelete()\">\r\n Annulla\r\n </button>\r\n </span>\r\n } @else {\r\n <button\r\n type=\"button\"\r\n class=\"fb-btn fb-btn--ghost fb-btn--icon\"\r\n [disabled]=\"isBusy()\"\r\n (click)=\"requestDelete(version)\"\r\n >\r\n Elimina\r\n </button>\r\n }\r\n }\r\n </div>\r\n </li>\r\n } @empty {\r\n <li class=\"fb-empty\">Nessuna versione: il flow non e\u2019 ancora stato salvato.</li>\r\n }\r\n </ul>\r\n\r\n <p class=\"fb-ver__note\">\r\n Una sola versione e\u2019 attiva: attivarne una rende superata la precedente, senza doverla disattivare prima.\r\n Le versioni superate costano poco \u2014 lo stato \u00ABSuperata\u00BB esiste per questo.\r\n </p>\r\n</div>\r\n", styles: [":host{display:flex;flex-direction:column;height:100%;min-height:0;background:var(--fb-surface, #fff)}.fb-ver__header{display:flex;align-items:center;justify-content:space-between;padding:10px 12px;border-bottom:1px solid var(--fb-border-subtle, #e6e9ee)}.fb-ver__title{margin:0;font-size:14px;font-weight:600;color:var(--fb-text, #1d2939)}.fb-ver__body{flex:1;min-height:0;overflow-y:auto;padding:10px 12px}.fb-ver__list{margin:12px 0 0;padding:0;list-style:none}.fb-ver__item{margin-bottom:6px;padding:8px;border:1px solid var(--fb-border-subtle, #e6e9ee);border-radius:6px;background:var(--fb-surface-alt, #f8f9fb)}.fb-ver__item--current{border-color:var(--fb-accent, #2f6feb);background:color-mix(in srgb,var(--fb-accent, #2f6feb) 5%,transparent)}.fb-ver__open{display:flex;align-items:center;gap:6px;width:100%;padding:0;border:0;background:transparent;color:var(--fb-text, #1d2939);font:inherit;text-align:left;cursor:pointer}.fb-ver__number{font-family:ui-monospace,SFMono-Regular,Menlo,monospace;font-size:13px;font-weight:700}.fb-ver__status{padding:1px 6px;border-radius:8px;background:var(--fb-border, #d6dae1);font-size:10px;font-weight:600;color:var(--fb-text, #1d2939)}.fb-ver__status--active{background:color-mix(in srgb,var(--fb-success, #3f8f5f) 18%,transparent);color:var(--fb-success, #3f8f5f)}.fb-ver__status--invalid{background:color-mix(in srgb,var(--fb-warning, #b7791f) 16%,transparent);color:var(--fb-warning, #b7791f)}.fb-ver__badge{font-size:9px;color:var(--fb-accent, #2f6feb);text-transform:uppercase;letter-spacing:.05em}.fb-ver__meta{margin:3px 0 0;font-size:10px;color:var(--fb-text-muted, #667085)}.fb-ver__actions{display:flex;flex-wrap:wrap;align-items:center;gap:6px;margin-top:6px}.fb-ver__hint{font-size:10px;color:var(--fb-text-subtle, #98a2b3)}.fb-ver__confirm{display:flex;flex-wrap:wrap;align-items:center;gap:4px;padding:5px 7px;border-radius:5px;background:color-mix(in srgb,var(--fb-error, #c9372c) 8%,transparent);font-size:10px;color:var(--fb-error, #c9372c)}.fb-ver__note{margin:12px 0 0;font-size:10px;line-height:1.45;color:var(--fb-text-subtle, #98a2b3)}\n"] }]
6465
7208
  }], propDecorators: { closed: [{ type: i0.Output, args: ["closed"] }], versionOpened: [{ type: i0.Output, args: ["versionOpened"] }], notice: [{ type: i0.Output, args: ["notice"] }] } });
6466
7209
 
6467
7210
  /**
6468
7211
  * Provare un flow dall'editor — FRONTEND.md §6.5, §11 ("Provare").
6469
7212
  *
6470
- * Cinque cose che questo pannello tratta come dette:
7213
+ * Sei cose che questo pannello tratta come dette:
6471
7214
  * 1. `status: 'Failed'` **non** e' un errore di trasporto: la richiesta e' riuscita, il flow
6472
7215
  * e' fallito. E `NotStarted` significa che i criteri dello Start non erano soddisfatti,
6473
7216
  * che non e' un errore.
@@ -6479,6 +7222,10 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.27", ngImpo
6479
7222
  * sono definitivi, e il pannello lo dice.
6480
7223
  * 5. `debug: true` popola `trace` e `resources` — **e riporta anche i dati personali**:
6481
7224
  * l'avviso e' in chiaro, non nascosto in una tooltip.
7225
+ * 6. `Suspended` copre due attese diverse — un evento e uno step di orchestrazione — e le
7226
+ * distinguono `isWaitingForEvent` / `isWaitingForStageStep`. Nel secondo caso si puo'
7227
+ * concludere lo step da qui, e la risposta puo' portare una `interviewKey` **nuova**: la
7228
+ * vecchia non e' piu' valida, quindi si sostituisce invece di conservarla (§5.13).
6482
7229
  */
6483
7230
  class DebugPanelComponent {
6484
7231
  api = inject(FlowBuilderApi);
@@ -6501,6 +7248,13 @@ class DebugPanelComponent {
6501
7248
  status = computed(() => this.result()?.status ?? null, ...(ngDevMode ? [{ debugName: "status" }] : []));
6502
7249
  pendingScreen = computed(() => this.result()?.pendingScreen ?? null, ...(ngDevMode ? [{ debugName: "pendingScreen" }] : []));
6503
7250
  isWaitingForScreen = computed(() => this.status() === 'WaitingForScreen', ...(ngDevMode ? [{ debugName: "isWaitingForScreen" }] : []));
7251
+ /** §5.13 — l'attesa e' su uno step assegnato a una persona, non su un evento. */
7252
+ isWaitingForStageStep = computed(() => this.result()?.isWaitingForStageStep === true, ...(ngDevMode ? [{ debugName: "isWaitingForStageStep" }] : []));
7253
+ stageSteps = computed(() => this.result()?.stageSteps ?? [], ...(ngDevMode ? [{ debugName: "stageSteps" }] : []));
7254
+ /** Lo step su cui c'e' un work item aperto: e' quello che si puo' concludere. */
7255
+ waitingStageSteps = computed(() => this.stageSteps().filter((step) => step.isWaiting), ...(ngDevMode ? [{ debugName: "waitingStageSteps" }] : []));
7256
+ /** I valori con cui si conclude uno step, per nome di output. */
7257
+ stepOutputs = signal({}, ...(ngDevMode ? [{ debugName: "stepOutputs" }] : []));
6504
7258
  trace = computed(() => this.result()?.trace ?? [], ...(ngDevMode ? [{ debugName: "trace" }] : []));
6505
7259
  /** Le risorse come righe ordinate: il tipo dichiarato vince in lettura (§6.5). */
6506
7260
  resourceRows = computed(() => {
@@ -6648,6 +7402,53 @@ class DebugPanelComponent {
6648
7402
  this.isRunning.set(false);
6649
7403
  }
6650
7404
  }
7405
+ setStepOutput(name, value) {
7406
+ this.stepOutputs.update((values) => ({ ...values, [name]: value }));
7407
+ }
7408
+ /** Gli output dichiarati dallo step, per generare i campi da compilare. */
7409
+ stepOutputNames(stepName) {
7410
+ if (!stepName) {
7411
+ return [];
7412
+ }
7413
+ for (const stage of this.store.document().orchestratedStages ?? []) {
7414
+ const step = stage.stageSteps?.find((candidate) => candidate.name === stepName);
7415
+ if (step) {
7416
+ return (step.outputParameters ?? []).map((parameter) => parameter.name ?? '').filter(Boolean);
7417
+ }
7418
+ }
7419
+ return [];
7420
+ }
7421
+ /**
7422
+ * Conclude uno step come farebbe l'assegnatario. `Rejected` non e' un errore: e' l'esito che
7423
+ * prende il ramo «Step rifiutato» dello stage — e se quel ramo non c'e', l'interview fallisce.
7424
+ */
7425
+ async completeStep(stepName, status) {
7426
+ const key = this.result()?.interviewKey;
7427
+ if (!key || !stepName) {
7428
+ return;
7429
+ }
7430
+ this.isRunning.set(true);
7431
+ this.errorMessage.set(null);
7432
+ try {
7433
+ const result = await this.api.completeStageStep({
7434
+ interviewKey: key,
7435
+ stepName,
7436
+ status,
7437
+ stepOutputs: this.stepOutputs(),
7438
+ debug: this.debugEnabled(),
7439
+ });
7440
+ this.stepOutputs.set({});
7441
+ // La risposta puo' portare una chiave nuova: `apply` sostituisce il risultato intero,
7442
+ // quindi la vecchia chiave non resta in giro.
7443
+ this.apply(result);
7444
+ }
7445
+ catch (error) {
7446
+ this.handleError(error);
7447
+ }
7448
+ finally {
7449
+ this.isRunning.set(false);
7450
+ }
7451
+ }
6651
7452
  apply(result) {
6652
7453
  this.result.set(result);
6653
7454
  if (result.currentElementName) {
@@ -6666,6 +7467,7 @@ class DebugPanelComponent {
6666
7467
  this.result.set(null);
6667
7468
  this.errorMessage.set(null);
6668
7469
  this.screenOutputs.set({});
7470
+ this.stepOutputs.set({});
6669
7471
  }
6670
7472
  close() {
6671
7473
  this.closed.emit();
@@ -6696,8 +7498,15 @@ class DebugPanelComponent {
6696
7498
  case 'NotStarted':
6697
7499
  return 'I criteri dello Start non erano soddisfatti: non e’ un errore.';
6698
7500
  case 'Paused':
7501
+ return 'L’esecuzione e’ in pausa: puo’ essere ripresa dalla sua chiave.';
6699
7502
  case 'Suspended':
6700
- return 'L’esecuzione e’ sospesa: puo’ essere ripresa dalla sua chiave.';
7503
+ // Due attese diverse sotto lo stesso stato: dirlo evita di cercare l'evento sbagliato.
7504
+ if (this.isWaitingForStageStep()) {
7505
+ return 'Sospesa su uno step di orchestrazione: aspetta che un assegnatario lo concluda.';
7506
+ }
7507
+ return this.result()?.isWaitingForEvent
7508
+ ? 'Sospesa in attesa di un evento: puo’ essere ripresa dalla sua chiave.'
7509
+ : 'L’esecuzione e’ sospesa: puo’ essere ripresa dalla sua chiave.';
6701
7510
  default:
6702
7511
  return null;
6703
7512
  }
@@ -6712,11 +7521,11 @@ class DebugPanelComponent {
6712
7521
  return this.pendingScreen()?.canPause === true;
6713
7522
  }
6714
7523
  static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.3.27", ngImport: i0, type: DebugPanelComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
6715
- static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "20.3.27", type: DebugPanelComponent, isStandalone: true, selector: "fb-debug-panel", outputs: { closed: "closed", elementFocused: "elementFocused" }, ngImport: i0, template: "<header class=\"fb-dbg__header\">\n <h2 class=\"fb-dbg__title\">Prova</h2>\n <button type=\"button\" class=\"fb-btn fb-btn--ghost fb-btn--icon\" aria-label=\"Chiudi\" (click)=\"close()\">\u00D7</button>\n</header>\n\n<div class=\"fb-dbg__body\">\n @if (errorMessage()) {\n <p class=\"fb-callout fb-callout--error\">{{ errorMessage() }}</p>\n }\n\n @if (!result()) {\n <fieldset class=\"fb-section\">\n <legend class=\"fb-section__title\">Valori iniziali</legend>\n @if (inputVariables().length) {\n @for (variable of inputVariables(); track variable.name) {\n <div class=\"fb-field\">\n <label class=\"fb-field__label\">\n {{ variable.name }}\n <span class=\"fb-dbg__type\">{{ variable.dataType }}{{ variable.isCollection ? '[]' : '' }}</span>\n </label>\n @if (variable.dataType === 'Boolean') {\n <select class=\"fb-select\" (change)=\"setInputValue(variable.name!, $any($event.target).value)\">\n <option value=\"\">\u2014</option>\n <option value=\"true\">vero</option>\n <option value=\"false\">falso</option>\n </select>\n } @else {\n <input\n class=\"fb-input\"\n [type]=\"variable.dataType === 'Number' || variable.dataType === 'Integer' ? 'number' : 'text'\"\n (input)=\"setInputValue(variable.name!, $any($event.target).value)\"\n />\n }\n </div>\n }\n } @else {\n <p class=\"fb-field__hint\">Il flow non dichiara variabili di input.</p>\n }\n </fieldset>\n\n <label class=\"fb-check\">\n <input\n type=\"checkbox\"\n [checked]=\"debugEnabled()\"\n (change)=\"setDebugEnabled($any($event.target).checked)\"\n />\n Traccia di debug\n </label>\n @if (debugEnabled()) {\n <p class=\"fb-callout fb-callout--warn\">\n La traccia riporta i valori di <strong>tutte</strong> le risorse, dati personali compresi: non usarla\n su dati reali.\n </p>\n }\n\n <button type=\"button\" class=\"fb-btn fb-btn--primary\" [disabled]=\"!canRun()\" (click)=\"start()\">\n Avvia\n </button>\n @if (!canRun() && !isRunning()) {\n <p class=\"fb-field__hint\">Salva il flow prima di provarlo.</p>\n }\n }\n\n @if (result()) {\n <div class=\"fb-dbg__status\">\n <span\n class=\"fb-dbg__badge\"\n [class.fb-dbg__badge--ok]=\"status() === 'Completed'\"\n [class.fb-dbg__badge--fail]=\"status() === 'Failed'\"\n [class.fb-dbg__badge--wait]=\"isWaitingForScreen()\"\n >\n {{ statusLabel() }}\n </span>\n @if (result()?.currentElementName) {\n <span class=\"fb-dbg__current\">su {{ result()?.currentElementName }}</span>\n }\n <span class=\"fb-dbg__steps\">{{ result()?.steps || 0 }} passi</span>\n <span class=\"fb-list__spacer\"></span>\n <button type=\"button\" class=\"fb-btn fb-btn--ghost fb-btn--icon\" (click)=\"reset()\">Riavvia</button>\n </div>\n\n @if (statusNote()) {\n <p class=\"fb-callout\">{{ statusNote() }}</p>\n }\n\n @if (result()?.fault) {\n <p class=\"fb-callout fb-callout--error\">{{ result()?.fault }}</p>\n }\n @for (message of result()?.errors || []; track message) {\n <p class=\"fb-callout fb-callout--error\">{{ message }}</p>\n }\n\n @if (isWaitingForScreen() && pendingScreen()) {\n <fieldset class=\"fb-section\">\n <legend class=\"fb-section__title\">\n Form \u00AB{{ pendingScreen()?.formName }}\u00BB\n </legend>\n <p class=\"fb-section__note\">\n Nell\u2019editor basta un form generico: qui vedi i valori che il form riceve e puoi compilare quelli\n che dichiara di restituire.\n </p>\n\n @if (pendingScreen()?.label) {\n <p class=\"fb-dbg__screen-label\">{{ pendingScreen()?.label }}</p>\n }\n @if (pendingScreen()?.helpText) {\n <p class=\"fb-field__hint\">{{ pendingScreen()?.helpText }}</p>\n }\n\n @if (screenInputRows().length) {\n <table class=\"fb-dbg__table\">\n <caption>\n Valori in ingresso\n </caption>\n <tbody>\n @for (row of screenInputRows(); track row.name) {\n <tr>\n <th scope=\"row\">{{ row.name }}</th>\n <td class=\"fb-dbg__type\">{{ row.type }}</td>\n <td>{{ row.value }}</td>\n </tr>\n }\n </tbody>\n </table>\n }\n\n @for (output of screenOutputNames(); track output) {\n <div class=\"fb-field\">\n <label class=\"fb-field__label\">{{ output }}</label>\n <input class=\"fb-input\" (input)=\"setScreenOutput(output, $any($event.target).value)\" />\n </div>\n }\n @if (!screenOutputNames().length) {\n <p class=\"fb-field__hint\">Lo screen non dichiara parametri di uscita.</p>\n }\n\n <div class=\"fb-field__row\">\n <button\n type=\"button\"\n class=\"fb-btn fb-btn--primary\"\n [disabled]=\"isRunning()\"\n (click)=\"respond('Next')\"\n >\n Avanti\n </button>\n <!-- canGoBack/canFinish/canPause sono la verita', piu' precisa dei flag del metadata. -->\n <button\n type=\"button\"\n class=\"fb-btn\"\n [disabled]=\"isRunning() || !canGoBack()\"\n title=\"Con \u00ABindietro\u00BB gli output non vengono memorizzati\"\n (click)=\"respond('Previous')\"\n >\n Indietro\n </button>\n <button type=\"button\" class=\"fb-btn\" [disabled]=\"isRunning() || !canFinish()\" (click)=\"respond('Finish')\">\n Fine\n </button>\n <button\n type=\"button\"\n class=\"fb-btn\"\n [disabled]=\"isRunning() || !canPause()\"\n title=\"Con \u00ABpausa\u00BB gli output non vengono memorizzati\"\n (click)=\"respond('Pause')\"\n >\n Pausa\n </button>\n </div>\n <p class=\"fb-field__hint\">\n Con \u00ABindietro\u00BB e \u00ABpausa\u00BB i valori inseriti <strong>non</strong> vengono memorizzati.\n </p>\n </fieldset>\n }\n\n @if (outputRows().length) {\n <fieldset class=\"fb-section\">\n <legend class=\"fb-section__title\">Output del flow</legend>\n <table class=\"fb-dbg__table\">\n <tbody>\n @for (row of outputRows(); track row.name) {\n <tr>\n <th scope=\"row\">{{ row.name }}</th>\n <td class=\"fb-dbg__type\">{{ row.type }}</td>\n <td>{{ row.value }}</td>\n </tr>\n }\n </tbody>\n </table>\n </fieldset>\n }\n\n @if (trace().length) {\n <fieldset class=\"fb-section\">\n <legend class=\"fb-section__title\">Traccia</legend>\n <ol class=\"fb-dbg__trace\">\n @for (entry of trace(); track entry.sequence) {\n <li class=\"fb-dbg__trace-item\">\n <span class=\"fb-dbg__trace-seq\">{{ entry.sequence }}</span>\n @if (entry.elementName) {\n <button type=\"button\" class=\"fb-dbg__trace-el\" (click)=\"elementFocused.emit(entry.elementName!)\">\n {{ entry.elementName }}\n </button>\n }\n <span class=\"fb-dbg__trace-msg\">{{ entry.message }}</span>\n </li>\n }\n </ol>\n </fieldset>\n }\n\n @if (resourceRows().length) {\n <fieldset class=\"fb-section\">\n <legend class=\"fb-section__title\">Risorse</legend>\n <table class=\"fb-dbg__table\">\n <tbody>\n @for (row of resourceRows(); track row.name) {\n <tr>\n <th scope=\"row\">{{ row.name }}</th>\n <td class=\"fb-dbg__type\">{{ row.type }}</td>\n <td>{{ row.value }}</td>\n </tr>\n }\n </tbody>\n </table>\n </fieldset>\n }\n\n @if (result()?.interviewKey) {\n <p class=\"fb-field__hint\">\n Chiave dell\u2019esecuzione sospesa: <code>{{ result()?.interviewKey }}</code>\n </p>\n }\n }\n</div>\n", styles: [":host{display:flex;flex-direction:column;height:100%;min-height:0;background:var(--fb-surface, #fff)}.fb-dbg__header{display:flex;align-items:center;justify-content:space-between;padding:10px 12px;border-bottom:1px solid var(--fb-border-subtle, #e6e9ee)}.fb-dbg__title{margin:0;font-size:14px;font-weight:600;color:var(--fb-text, #1d2939)}.fb-dbg__body{flex:1;min-height:0;overflow-y:auto;padding:10px 12px}.fb-dbg__status{display:flex;align-items:center;gap:8px;margin-bottom:10px}.fb-dbg__badge{padding:2px 8px;border-radius:10px;background:var(--fb-border, #d6dae1);font-size:11px;font-weight:600;color:var(--fb-text, #1d2939)}.fb-dbg__badge--ok{background:color-mix(in srgb,var(--fb-success, #3f8f5f) 18%,transparent);color:var(--fb-success, #3f8f5f)}.fb-dbg__badge--fail{background:color-mix(in srgb,var(--fb-error, #c9372c) 14%,transparent);color:var(--fb-error, #c9372c)}.fb-dbg__badge--wait{background:color-mix(in srgb,var(--fb-accent, #2f6feb) 12%,transparent);color:var(--fb-accent, #2f6feb)}.fb-dbg__current,.fb-dbg__steps{font-size:10px;color:var(--fb-text-muted, #667085)}.fb-dbg__type{font-family:ui-monospace,SFMono-Regular,Menlo,monospace;font-size:9px;color:var(--fb-text-subtle, #98a2b3)}.fb-dbg__screen-label{margin:0 0 4px;font-size:12px;font-weight:600;color:var(--fb-text, #1d2939)}.fb-dbg__table{width:100%;border-collapse:collapse;font-size:11px}.fb-dbg__table caption{padding-bottom:3px;font-size:10px;color:var(--fb-text-subtle, #98a2b3);text-align:left}.fb-dbg__table th,.fb-dbg__table td{padding:3px 5px;border-bottom:1px solid var(--fb-border-subtle, #e6e9ee);text-align:left;vertical-align:top}.fb-dbg__table th{font-family:ui-monospace,SFMono-Regular,Menlo,monospace;font-weight:600;color:var(--fb-text, #1d2939)}.fb-dbg__table td{color:var(--fb-text-muted, #667085);word-break:break-word}.fb-dbg__trace{margin:0;padding:0;list-style:none}.fb-dbg__trace-item{display:flex;gap:6px;padding:3px 0;border-bottom:1px solid var(--fb-border-subtle, #e6e9ee);font-size:11px}.fb-dbg__trace-seq{flex:0 0 auto;width:18px;font-family:ui-monospace,SFMono-Regular,Menlo,monospace;font-size:9px;color:var(--fb-text-subtle, #98a2b3);text-align:right}.fb-dbg__trace-el{flex:0 0 auto;padding:0;border:0;background:transparent;color:var(--fb-accent, #2f6feb);font:inherit;font-size:10px;cursor:pointer;text-decoration:underline}.fb-dbg__trace-msg{color:var(--fb-text-muted, #667085);line-height:1.35}\n"], changeDetection: i0.ChangeDetectionStrategy.OnPush });
7524
+ static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "20.3.27", type: DebugPanelComponent, isStandalone: true, selector: "fb-debug-panel", outputs: { closed: "closed", elementFocused: "elementFocused" }, ngImport: i0, template: "<header class=\"fb-dbg__header\">\r\n <h2 class=\"fb-dbg__title\">Prova</h2>\r\n <button type=\"button\" class=\"fb-btn fb-btn--ghost fb-btn--icon\" aria-label=\"Chiudi\" (click)=\"close()\">\u00D7</button>\r\n</header>\r\n\r\n<div class=\"fb-dbg__body\">\r\n @if (errorMessage()) {\r\n <p class=\"fb-callout fb-callout--error\">{{ errorMessage() }}</p>\r\n }\r\n\r\n @if (!result()) {\r\n <fieldset class=\"fb-section\">\r\n <legend class=\"fb-section__title\">Valori iniziali</legend>\r\n @if (inputVariables().length) {\r\n @for (variable of inputVariables(); track variable.name) {\r\n <div class=\"fb-field\">\r\n <label class=\"fb-field__label\">\r\n {{ variable.name }}\r\n <span class=\"fb-dbg__type\">{{ variable.dataType }}{{ variable.isCollection ? '[]' : '' }}</span>\r\n </label>\r\n @if (variable.dataType === 'Boolean') {\r\n <select class=\"fb-select\" (change)=\"setInputValue(variable.name!, $any($event.target).value)\">\r\n <option value=\"\">\u2014</option>\r\n <option value=\"true\">vero</option>\r\n <option value=\"false\">falso</option>\r\n </select>\r\n } @else {\r\n <input\r\n class=\"fb-input\"\r\n [type]=\"variable.dataType === 'Number' || variable.dataType === 'Integer' ? 'number' : 'text'\"\r\n (input)=\"setInputValue(variable.name!, $any($event.target).value)\"\r\n />\r\n }\r\n </div>\r\n }\r\n } @else {\r\n <p class=\"fb-field__hint\">Il flow non dichiara variabili di input.</p>\r\n }\r\n </fieldset>\r\n\r\n <label class=\"fb-check\">\r\n <input\r\n type=\"checkbox\"\r\n [checked]=\"debugEnabled()\"\r\n (change)=\"setDebugEnabled($any($event.target).checked)\"\r\n />\r\n Traccia di debug\r\n </label>\r\n @if (debugEnabled()) {\r\n <p class=\"fb-callout fb-callout--warn\">\r\n La traccia riporta i valori di <strong>tutte</strong> le risorse, dati personali compresi: non usarla\r\n su dati reali.\r\n </p>\r\n }\r\n\r\n <button type=\"button\" class=\"fb-btn fb-btn--primary\" [disabled]=\"!canRun()\" (click)=\"start()\">\r\n Avvia\r\n </button>\r\n @if (!canRun() && !isRunning()) {\r\n <p class=\"fb-field__hint\">Salva il flow prima di provarlo.</p>\r\n }\r\n }\r\n\r\n @if (result()) {\r\n <div class=\"fb-dbg__status\">\r\n <span\r\n class=\"fb-dbg__badge\"\r\n [class.fb-dbg__badge--ok]=\"status() === 'Completed'\"\r\n [class.fb-dbg__badge--fail]=\"status() === 'Failed'\"\r\n [class.fb-dbg__badge--wait]=\"isWaitingForScreen()\"\r\n >\r\n {{ statusLabel() }}\r\n </span>\r\n @if (result()?.currentElementName) {\r\n <span class=\"fb-dbg__current\">su {{ result()?.currentElementName }}</span>\r\n }\r\n <span class=\"fb-dbg__steps\">{{ result()?.steps || 0 }} passi</span>\r\n <span class=\"fb-list__spacer\"></span>\r\n <button type=\"button\" class=\"fb-btn fb-btn--ghost fb-btn--icon\" (click)=\"reset()\">Riavvia</button>\r\n </div>\r\n\r\n @if (statusNote()) {\r\n <p class=\"fb-callout\">{{ statusNote() }}</p>\r\n }\r\n\r\n @if (result()?.fault) {\r\n <p class=\"fb-callout fb-callout--error\">{{ result()?.fault }}</p>\r\n }\r\n @for (message of result()?.errors || []; track message) {\r\n <p class=\"fb-callout fb-callout--error\">{{ message }}</p>\r\n }\r\n\r\n @if (isWaitingForScreen() && pendingScreen()) {\r\n <fieldset class=\"fb-section\">\r\n <legend class=\"fb-section__title\">\r\n Form \u00AB{{ pendingScreen()?.formName }}\u00BB\r\n </legend>\r\n <p class=\"fb-section__note\">\r\n Nell\u2019editor basta un form generico: qui vedi i valori che il form riceve e puoi compilare quelli\r\n che dichiara di restituire.\r\n </p>\r\n\r\n @if (pendingScreen()?.label) {\r\n <p class=\"fb-dbg__screen-label\">{{ pendingScreen()?.label }}</p>\r\n }\r\n @if (pendingScreen()?.helpText) {\r\n <p class=\"fb-field__hint\">{{ pendingScreen()?.helpText }}</p>\r\n }\r\n\r\n @if (screenInputRows().length) {\r\n <table class=\"fb-dbg__table\">\r\n <caption>\r\n Valori in ingresso\r\n </caption>\r\n <tbody>\r\n @for (row of screenInputRows(); track row.name) {\r\n <tr>\r\n <th scope=\"row\">{{ row.name }}</th>\r\n <td class=\"fb-dbg__type\">{{ row.type }}</td>\r\n <td>{{ row.value }}</td>\r\n </tr>\r\n }\r\n </tbody>\r\n </table>\r\n }\r\n\r\n @for (output of screenOutputNames(); track output) {\r\n <div class=\"fb-field\">\r\n <label class=\"fb-field__label\">{{ output }}</label>\r\n <input class=\"fb-input\" (input)=\"setScreenOutput(output, $any($event.target).value)\" />\r\n </div>\r\n }\r\n @if (!screenOutputNames().length) {\r\n <p class=\"fb-field__hint\">Lo screen non dichiara parametri di uscita.</p>\r\n }\r\n\r\n <div class=\"fb-field__row\">\r\n <button\r\n type=\"button\"\r\n class=\"fb-btn fb-btn--primary\"\r\n [disabled]=\"isRunning()\"\r\n (click)=\"respond('Next')\"\r\n >\r\n Avanti\r\n </button>\r\n <!-- canGoBack/canFinish/canPause sono la verita', piu' precisa dei flag del metadata. -->\r\n <button\r\n type=\"button\"\r\n class=\"fb-btn\"\r\n [disabled]=\"isRunning() || !canGoBack()\"\r\n title=\"Con \u00ABindietro\u00BB gli output non vengono memorizzati\"\r\n (click)=\"respond('Previous')\"\r\n >\r\n Indietro\r\n </button>\r\n <button type=\"button\" class=\"fb-btn\" [disabled]=\"isRunning() || !canFinish()\" (click)=\"respond('Finish')\">\r\n Fine\r\n </button>\r\n <button\r\n type=\"button\"\r\n class=\"fb-btn\"\r\n [disabled]=\"isRunning() || !canPause()\"\r\n title=\"Con \u00ABpausa\u00BB gli output non vengono memorizzati\"\r\n (click)=\"respond('Pause')\"\r\n >\r\n Pausa\r\n </button>\r\n </div>\r\n <p class=\"fb-field__hint\">\r\n Con \u00ABindietro\u00BB e \u00ABpausa\u00BB i valori inseriti <strong>non</strong> vengono memorizzati.\r\n </p>\r\n </fieldset>\r\n }\r\n\r\n @if (isWaitingForStageStep()) {\r\n <fieldset class=\"fb-section\">\r\n <legend class=\"fb-section__title\">Step di orchestrazione</legend>\r\n <p class=\"fb-section__note\">\r\n L\u2019interview e\u2019 sospesa su uno step assegnato: qui si conclude al posto dell\u2019assegnatario.\r\n Concludere puo\u2019 far <strong>sospendere di nuovo</strong> lo stage, con una chiave nuova.\r\n </p>\r\n\r\n <table class=\"fb-dbg__table\">\r\n <tbody>\r\n @for (step of stageSteps(); track step.stepName) {\r\n <tr>\r\n <th scope=\"row\">{{ step.label || step.stepName }}</th>\r\n <td class=\"fb-dbg__type\">{{ step.actionType }}</td>\r\n <td>{{ step.isWaiting ? 'in attesa' : step.status }}</td>\r\n </tr>\r\n }\r\n </tbody>\r\n </table>\r\n\r\n @for (step of waitingStageSteps(); track step.stepName) {\r\n <div class=\"fb-list__item\">\r\n <div class=\"fb-list__header\">\r\n <span class=\"fb-list__title\">{{ step.label || step.stepName }}</span>\r\n </div>\r\n @for (output of stepOutputNames(step.stepName); track output) {\r\n <div class=\"fb-field\">\r\n <label class=\"fb-field__label\">{{ output }}</label>\r\n <input class=\"fb-input\" (input)=\"setStepOutput(output, $any($event.target).value)\" />\r\n </div>\r\n }\r\n <div class=\"fb-field__row\">\r\n <button\r\n type=\"button\"\r\n class=\"fb-btn fb-btn--primary\"\r\n [disabled]=\"isRunning()\"\r\n (click)=\"completeStep(step.stepName, 'Completed')\"\r\n >\r\n Concludi\r\n </button>\r\n <!-- Il rifiuto non e' un errore: prende il ramo \u00ABStep rifiutato\u00BB dello stage. -->\r\n <button\r\n type=\"button\"\r\n class=\"fb-btn\"\r\n [disabled]=\"isRunning()\"\r\n title=\"Prende il ramo \u00ABStep rifiutato\u00BB; senza quel ramo l\u2019interview fallisce\"\r\n (click)=\"completeStep(step.stepName, 'Rejected')\"\r\n >\r\n Rifiuta\r\n </button>\r\n <button\r\n type=\"button\"\r\n class=\"fb-btn fb-btn--ghost\"\r\n [disabled]=\"isRunning()\"\r\n (click)=\"completeStep(step.stepName, 'Cancelled')\"\r\n >\r\n Annulla lo step\r\n </button>\r\n </div>\r\n </div>\r\n }\r\n </fieldset>\r\n }\r\n\r\n @if (outputRows().length) {\r\n <fieldset class=\"fb-section\">\r\n <legend class=\"fb-section__title\">Output del flow</legend>\r\n <table class=\"fb-dbg__table\">\r\n <tbody>\r\n @for (row of outputRows(); track row.name) {\r\n <tr>\r\n <th scope=\"row\">{{ row.name }}</th>\r\n <td class=\"fb-dbg__type\">{{ row.type }}</td>\r\n <td>{{ row.value }}</td>\r\n </tr>\r\n }\r\n </tbody>\r\n </table>\r\n </fieldset>\r\n }\r\n\r\n @if (trace().length) {\r\n <fieldset class=\"fb-section\">\r\n <legend class=\"fb-section__title\">Traccia</legend>\r\n <ol class=\"fb-dbg__trace\">\r\n @for (entry of trace(); track entry.sequence) {\r\n <li class=\"fb-dbg__trace-item\">\r\n <span class=\"fb-dbg__trace-seq\">{{ entry.sequence }}</span>\r\n @if (entry.elementName) {\r\n <button type=\"button\" class=\"fb-dbg__trace-el\" (click)=\"elementFocused.emit(entry.elementName!)\">\r\n {{ entry.elementName }}\r\n </button>\r\n }\r\n <span class=\"fb-dbg__trace-msg\">{{ entry.message }}</span>\r\n </li>\r\n }\r\n </ol>\r\n </fieldset>\r\n }\r\n\r\n @if (resourceRows().length) {\r\n <fieldset class=\"fb-section\">\r\n <legend class=\"fb-section__title\">Risorse</legend>\r\n <table class=\"fb-dbg__table\">\r\n <tbody>\r\n @for (row of resourceRows(); track row.name) {\r\n <tr>\r\n <th scope=\"row\">{{ row.name }}</th>\r\n <td class=\"fb-dbg__type\">{{ row.type }}</td>\r\n <td>{{ row.value }}</td>\r\n </tr>\r\n }\r\n </tbody>\r\n </table>\r\n </fieldset>\r\n }\r\n\r\n @if (result()?.interviewKey) {\r\n <p class=\"fb-field__hint\">\r\n Chiave dell\u2019esecuzione sospesa: <code>{{ result()?.interviewKey }}</code>\r\n </p>\r\n }\r\n }\r\n</div>\r\n", styles: [":host{display:flex;flex-direction:column;height:100%;min-height:0;background:var(--fb-surface, #fff)}.fb-dbg__header{display:flex;align-items:center;justify-content:space-between;padding:10px 12px;border-bottom:1px solid var(--fb-border-subtle, #e6e9ee)}.fb-dbg__title{margin:0;font-size:14px;font-weight:600;color:var(--fb-text, #1d2939)}.fb-dbg__body{flex:1;min-height:0;overflow-y:auto;padding:10px 12px}.fb-dbg__status{display:flex;align-items:center;gap:8px;margin-bottom:10px}.fb-dbg__badge{padding:2px 8px;border-radius:10px;background:var(--fb-border, #d6dae1);font-size:11px;font-weight:600;color:var(--fb-text, #1d2939)}.fb-dbg__badge--ok{background:color-mix(in srgb,var(--fb-success, #3f8f5f) 18%,transparent);color:var(--fb-success, #3f8f5f)}.fb-dbg__badge--fail{background:color-mix(in srgb,var(--fb-error, #c9372c) 14%,transparent);color:var(--fb-error, #c9372c)}.fb-dbg__badge--wait{background:color-mix(in srgb,var(--fb-accent, #2f6feb) 12%,transparent);color:var(--fb-accent, #2f6feb)}.fb-dbg__current,.fb-dbg__steps{font-size:10px;color:var(--fb-text-muted, #667085)}.fb-dbg__type{font-family:ui-monospace,SFMono-Regular,Menlo,monospace;font-size:9px;color:var(--fb-text-subtle, #98a2b3)}.fb-dbg__screen-label{margin:0 0 4px;font-size:12px;font-weight:600;color:var(--fb-text, #1d2939)}.fb-dbg__table{width:100%;border-collapse:collapse;font-size:11px}.fb-dbg__table caption{padding-bottom:3px;font-size:10px;color:var(--fb-text-subtle, #98a2b3);text-align:left}.fb-dbg__table th,.fb-dbg__table td{padding:3px 5px;border-bottom:1px solid var(--fb-border-subtle, #e6e9ee);text-align:left;vertical-align:top}.fb-dbg__table th{font-family:ui-monospace,SFMono-Regular,Menlo,monospace;font-weight:600;color:var(--fb-text, #1d2939)}.fb-dbg__table td{color:var(--fb-text-muted, #667085);word-break:break-word}.fb-dbg__trace{margin:0;padding:0;list-style:none}.fb-dbg__trace-item{display:flex;gap:6px;padding:3px 0;border-bottom:1px solid var(--fb-border-subtle, #e6e9ee);font-size:11px}.fb-dbg__trace-seq{flex:0 0 auto;width:18px;font-family:ui-monospace,SFMono-Regular,Menlo,monospace;font-size:9px;color:var(--fb-text-subtle, #98a2b3);text-align:right}.fb-dbg__trace-el{flex:0 0 auto;padding:0;border:0;background:transparent;color:var(--fb-accent, #2f6feb);font:inherit;font-size:10px;cursor:pointer;text-decoration:underline}.fb-dbg__trace-msg{color:var(--fb-text-muted, #667085);line-height:1.35}\n"], changeDetection: i0.ChangeDetectionStrategy.OnPush });
6716
7525
  }
6717
7526
  i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.27", ngImport: i0, type: DebugPanelComponent, decorators: [{
6718
7527
  type: Component,
6719
- args: [{ selector: 'fb-debug-panel', standalone: true, changeDetection: ChangeDetectionStrategy.OnPush, template: "<header class=\"fb-dbg__header\">\n <h2 class=\"fb-dbg__title\">Prova</h2>\n <button type=\"button\" class=\"fb-btn fb-btn--ghost fb-btn--icon\" aria-label=\"Chiudi\" (click)=\"close()\">\u00D7</button>\n</header>\n\n<div class=\"fb-dbg__body\">\n @if (errorMessage()) {\n <p class=\"fb-callout fb-callout--error\">{{ errorMessage() }}</p>\n }\n\n @if (!result()) {\n <fieldset class=\"fb-section\">\n <legend class=\"fb-section__title\">Valori iniziali</legend>\n @if (inputVariables().length) {\n @for (variable of inputVariables(); track variable.name) {\n <div class=\"fb-field\">\n <label class=\"fb-field__label\">\n {{ variable.name }}\n <span class=\"fb-dbg__type\">{{ variable.dataType }}{{ variable.isCollection ? '[]' : '' }}</span>\n </label>\n @if (variable.dataType === 'Boolean') {\n <select class=\"fb-select\" (change)=\"setInputValue(variable.name!, $any($event.target).value)\">\n <option value=\"\">\u2014</option>\n <option value=\"true\">vero</option>\n <option value=\"false\">falso</option>\n </select>\n } @else {\n <input\n class=\"fb-input\"\n [type]=\"variable.dataType === 'Number' || variable.dataType === 'Integer' ? 'number' : 'text'\"\n (input)=\"setInputValue(variable.name!, $any($event.target).value)\"\n />\n }\n </div>\n }\n } @else {\n <p class=\"fb-field__hint\">Il flow non dichiara variabili di input.</p>\n }\n </fieldset>\n\n <label class=\"fb-check\">\n <input\n type=\"checkbox\"\n [checked]=\"debugEnabled()\"\n (change)=\"setDebugEnabled($any($event.target).checked)\"\n />\n Traccia di debug\n </label>\n @if (debugEnabled()) {\n <p class=\"fb-callout fb-callout--warn\">\n La traccia riporta i valori di <strong>tutte</strong> le risorse, dati personali compresi: non usarla\n su dati reali.\n </p>\n }\n\n <button type=\"button\" class=\"fb-btn fb-btn--primary\" [disabled]=\"!canRun()\" (click)=\"start()\">\n Avvia\n </button>\n @if (!canRun() && !isRunning()) {\n <p class=\"fb-field__hint\">Salva il flow prima di provarlo.</p>\n }\n }\n\n @if (result()) {\n <div class=\"fb-dbg__status\">\n <span\n class=\"fb-dbg__badge\"\n [class.fb-dbg__badge--ok]=\"status() === 'Completed'\"\n [class.fb-dbg__badge--fail]=\"status() === 'Failed'\"\n [class.fb-dbg__badge--wait]=\"isWaitingForScreen()\"\n >\n {{ statusLabel() }}\n </span>\n @if (result()?.currentElementName) {\n <span class=\"fb-dbg__current\">su {{ result()?.currentElementName }}</span>\n }\n <span class=\"fb-dbg__steps\">{{ result()?.steps || 0 }} passi</span>\n <span class=\"fb-list__spacer\"></span>\n <button type=\"button\" class=\"fb-btn fb-btn--ghost fb-btn--icon\" (click)=\"reset()\">Riavvia</button>\n </div>\n\n @if (statusNote()) {\n <p class=\"fb-callout\">{{ statusNote() }}</p>\n }\n\n @if (result()?.fault) {\n <p class=\"fb-callout fb-callout--error\">{{ result()?.fault }}</p>\n }\n @for (message of result()?.errors || []; track message) {\n <p class=\"fb-callout fb-callout--error\">{{ message }}</p>\n }\n\n @if (isWaitingForScreen() && pendingScreen()) {\n <fieldset class=\"fb-section\">\n <legend class=\"fb-section__title\">\n Form \u00AB{{ pendingScreen()?.formName }}\u00BB\n </legend>\n <p class=\"fb-section__note\">\n Nell\u2019editor basta un form generico: qui vedi i valori che il form riceve e puoi compilare quelli\n che dichiara di restituire.\n </p>\n\n @if (pendingScreen()?.label) {\n <p class=\"fb-dbg__screen-label\">{{ pendingScreen()?.label }}</p>\n }\n @if (pendingScreen()?.helpText) {\n <p class=\"fb-field__hint\">{{ pendingScreen()?.helpText }}</p>\n }\n\n @if (screenInputRows().length) {\n <table class=\"fb-dbg__table\">\n <caption>\n Valori in ingresso\n </caption>\n <tbody>\n @for (row of screenInputRows(); track row.name) {\n <tr>\n <th scope=\"row\">{{ row.name }}</th>\n <td class=\"fb-dbg__type\">{{ row.type }}</td>\n <td>{{ row.value }}</td>\n </tr>\n }\n </tbody>\n </table>\n }\n\n @for (output of screenOutputNames(); track output) {\n <div class=\"fb-field\">\n <label class=\"fb-field__label\">{{ output }}</label>\n <input class=\"fb-input\" (input)=\"setScreenOutput(output, $any($event.target).value)\" />\n </div>\n }\n @if (!screenOutputNames().length) {\n <p class=\"fb-field__hint\">Lo screen non dichiara parametri di uscita.</p>\n }\n\n <div class=\"fb-field__row\">\n <button\n type=\"button\"\n class=\"fb-btn fb-btn--primary\"\n [disabled]=\"isRunning()\"\n (click)=\"respond('Next')\"\n >\n Avanti\n </button>\n <!-- canGoBack/canFinish/canPause sono la verita', piu' precisa dei flag del metadata. -->\n <button\n type=\"button\"\n class=\"fb-btn\"\n [disabled]=\"isRunning() || !canGoBack()\"\n title=\"Con \u00ABindietro\u00BB gli output non vengono memorizzati\"\n (click)=\"respond('Previous')\"\n >\n Indietro\n </button>\n <button type=\"button\" class=\"fb-btn\" [disabled]=\"isRunning() || !canFinish()\" (click)=\"respond('Finish')\">\n Fine\n </button>\n <button\n type=\"button\"\n class=\"fb-btn\"\n [disabled]=\"isRunning() || !canPause()\"\n title=\"Con \u00ABpausa\u00BB gli output non vengono memorizzati\"\n (click)=\"respond('Pause')\"\n >\n Pausa\n </button>\n </div>\n <p class=\"fb-field__hint\">\n Con \u00ABindietro\u00BB e \u00ABpausa\u00BB i valori inseriti <strong>non</strong> vengono memorizzati.\n </p>\n </fieldset>\n }\n\n @if (outputRows().length) {\n <fieldset class=\"fb-section\">\n <legend class=\"fb-section__title\">Output del flow</legend>\n <table class=\"fb-dbg__table\">\n <tbody>\n @for (row of outputRows(); track row.name) {\n <tr>\n <th scope=\"row\">{{ row.name }}</th>\n <td class=\"fb-dbg__type\">{{ row.type }}</td>\n <td>{{ row.value }}</td>\n </tr>\n }\n </tbody>\n </table>\n </fieldset>\n }\n\n @if (trace().length) {\n <fieldset class=\"fb-section\">\n <legend class=\"fb-section__title\">Traccia</legend>\n <ol class=\"fb-dbg__trace\">\n @for (entry of trace(); track entry.sequence) {\n <li class=\"fb-dbg__trace-item\">\n <span class=\"fb-dbg__trace-seq\">{{ entry.sequence }}</span>\n @if (entry.elementName) {\n <button type=\"button\" class=\"fb-dbg__trace-el\" (click)=\"elementFocused.emit(entry.elementName!)\">\n {{ entry.elementName }}\n </button>\n }\n <span class=\"fb-dbg__trace-msg\">{{ entry.message }}</span>\n </li>\n }\n </ol>\n </fieldset>\n }\n\n @if (resourceRows().length) {\n <fieldset class=\"fb-section\">\n <legend class=\"fb-section__title\">Risorse</legend>\n <table class=\"fb-dbg__table\">\n <tbody>\n @for (row of resourceRows(); track row.name) {\n <tr>\n <th scope=\"row\">{{ row.name }}</th>\n <td class=\"fb-dbg__type\">{{ row.type }}</td>\n <td>{{ row.value }}</td>\n </tr>\n }\n </tbody>\n </table>\n </fieldset>\n }\n\n @if (result()?.interviewKey) {\n <p class=\"fb-field__hint\">\n Chiave dell\u2019esecuzione sospesa: <code>{{ result()?.interviewKey }}</code>\n </p>\n }\n }\n</div>\n", styles: [":host{display:flex;flex-direction:column;height:100%;min-height:0;background:var(--fb-surface, #fff)}.fb-dbg__header{display:flex;align-items:center;justify-content:space-between;padding:10px 12px;border-bottom:1px solid var(--fb-border-subtle, #e6e9ee)}.fb-dbg__title{margin:0;font-size:14px;font-weight:600;color:var(--fb-text, #1d2939)}.fb-dbg__body{flex:1;min-height:0;overflow-y:auto;padding:10px 12px}.fb-dbg__status{display:flex;align-items:center;gap:8px;margin-bottom:10px}.fb-dbg__badge{padding:2px 8px;border-radius:10px;background:var(--fb-border, #d6dae1);font-size:11px;font-weight:600;color:var(--fb-text, #1d2939)}.fb-dbg__badge--ok{background:color-mix(in srgb,var(--fb-success, #3f8f5f) 18%,transparent);color:var(--fb-success, #3f8f5f)}.fb-dbg__badge--fail{background:color-mix(in srgb,var(--fb-error, #c9372c) 14%,transparent);color:var(--fb-error, #c9372c)}.fb-dbg__badge--wait{background:color-mix(in srgb,var(--fb-accent, #2f6feb) 12%,transparent);color:var(--fb-accent, #2f6feb)}.fb-dbg__current,.fb-dbg__steps{font-size:10px;color:var(--fb-text-muted, #667085)}.fb-dbg__type{font-family:ui-monospace,SFMono-Regular,Menlo,monospace;font-size:9px;color:var(--fb-text-subtle, #98a2b3)}.fb-dbg__screen-label{margin:0 0 4px;font-size:12px;font-weight:600;color:var(--fb-text, #1d2939)}.fb-dbg__table{width:100%;border-collapse:collapse;font-size:11px}.fb-dbg__table caption{padding-bottom:3px;font-size:10px;color:var(--fb-text-subtle, #98a2b3);text-align:left}.fb-dbg__table th,.fb-dbg__table td{padding:3px 5px;border-bottom:1px solid var(--fb-border-subtle, #e6e9ee);text-align:left;vertical-align:top}.fb-dbg__table th{font-family:ui-monospace,SFMono-Regular,Menlo,monospace;font-weight:600;color:var(--fb-text, #1d2939)}.fb-dbg__table td{color:var(--fb-text-muted, #667085);word-break:break-word}.fb-dbg__trace{margin:0;padding:0;list-style:none}.fb-dbg__trace-item{display:flex;gap:6px;padding:3px 0;border-bottom:1px solid var(--fb-border-subtle, #e6e9ee);font-size:11px}.fb-dbg__trace-seq{flex:0 0 auto;width:18px;font-family:ui-monospace,SFMono-Regular,Menlo,monospace;font-size:9px;color:var(--fb-text-subtle, #98a2b3);text-align:right}.fb-dbg__trace-el{flex:0 0 auto;padding:0;border:0;background:transparent;color:var(--fb-accent, #2f6feb);font:inherit;font-size:10px;cursor:pointer;text-decoration:underline}.fb-dbg__trace-msg{color:var(--fb-text-muted, #667085);line-height:1.35}\n"] }]
7528
+ args: [{ selector: 'fb-debug-panel', standalone: true, changeDetection: ChangeDetectionStrategy.OnPush, template: "<header class=\"fb-dbg__header\">\r\n <h2 class=\"fb-dbg__title\">Prova</h2>\r\n <button type=\"button\" class=\"fb-btn fb-btn--ghost fb-btn--icon\" aria-label=\"Chiudi\" (click)=\"close()\">\u00D7</button>\r\n</header>\r\n\r\n<div class=\"fb-dbg__body\">\r\n @if (errorMessage()) {\r\n <p class=\"fb-callout fb-callout--error\">{{ errorMessage() }}</p>\r\n }\r\n\r\n @if (!result()) {\r\n <fieldset class=\"fb-section\">\r\n <legend class=\"fb-section__title\">Valori iniziali</legend>\r\n @if (inputVariables().length) {\r\n @for (variable of inputVariables(); track variable.name) {\r\n <div class=\"fb-field\">\r\n <label class=\"fb-field__label\">\r\n {{ variable.name }}\r\n <span class=\"fb-dbg__type\">{{ variable.dataType }}{{ variable.isCollection ? '[]' : '' }}</span>\r\n </label>\r\n @if (variable.dataType === 'Boolean') {\r\n <select class=\"fb-select\" (change)=\"setInputValue(variable.name!, $any($event.target).value)\">\r\n <option value=\"\">\u2014</option>\r\n <option value=\"true\">vero</option>\r\n <option value=\"false\">falso</option>\r\n </select>\r\n } @else {\r\n <input\r\n class=\"fb-input\"\r\n [type]=\"variable.dataType === 'Number' || variable.dataType === 'Integer' ? 'number' : 'text'\"\r\n (input)=\"setInputValue(variable.name!, $any($event.target).value)\"\r\n />\r\n }\r\n </div>\r\n }\r\n } @else {\r\n <p class=\"fb-field__hint\">Il flow non dichiara variabili di input.</p>\r\n }\r\n </fieldset>\r\n\r\n <label class=\"fb-check\">\r\n <input\r\n type=\"checkbox\"\r\n [checked]=\"debugEnabled()\"\r\n (change)=\"setDebugEnabled($any($event.target).checked)\"\r\n />\r\n Traccia di debug\r\n </label>\r\n @if (debugEnabled()) {\r\n <p class=\"fb-callout fb-callout--warn\">\r\n La traccia riporta i valori di <strong>tutte</strong> le risorse, dati personali compresi: non usarla\r\n su dati reali.\r\n </p>\r\n }\r\n\r\n <button type=\"button\" class=\"fb-btn fb-btn--primary\" [disabled]=\"!canRun()\" (click)=\"start()\">\r\n Avvia\r\n </button>\r\n @if (!canRun() && !isRunning()) {\r\n <p class=\"fb-field__hint\">Salva il flow prima di provarlo.</p>\r\n }\r\n }\r\n\r\n @if (result()) {\r\n <div class=\"fb-dbg__status\">\r\n <span\r\n class=\"fb-dbg__badge\"\r\n [class.fb-dbg__badge--ok]=\"status() === 'Completed'\"\r\n [class.fb-dbg__badge--fail]=\"status() === 'Failed'\"\r\n [class.fb-dbg__badge--wait]=\"isWaitingForScreen()\"\r\n >\r\n {{ statusLabel() }}\r\n </span>\r\n @if (result()?.currentElementName) {\r\n <span class=\"fb-dbg__current\">su {{ result()?.currentElementName }}</span>\r\n }\r\n <span class=\"fb-dbg__steps\">{{ result()?.steps || 0 }} passi</span>\r\n <span class=\"fb-list__spacer\"></span>\r\n <button type=\"button\" class=\"fb-btn fb-btn--ghost fb-btn--icon\" (click)=\"reset()\">Riavvia</button>\r\n </div>\r\n\r\n @if (statusNote()) {\r\n <p class=\"fb-callout\">{{ statusNote() }}</p>\r\n }\r\n\r\n @if (result()?.fault) {\r\n <p class=\"fb-callout fb-callout--error\">{{ result()?.fault }}</p>\r\n }\r\n @for (message of result()?.errors || []; track message) {\r\n <p class=\"fb-callout fb-callout--error\">{{ message }}</p>\r\n }\r\n\r\n @if (isWaitingForScreen() && pendingScreen()) {\r\n <fieldset class=\"fb-section\">\r\n <legend class=\"fb-section__title\">\r\n Form \u00AB{{ pendingScreen()?.formName }}\u00BB\r\n </legend>\r\n <p class=\"fb-section__note\">\r\n Nell\u2019editor basta un form generico: qui vedi i valori che il form riceve e puoi compilare quelli\r\n che dichiara di restituire.\r\n </p>\r\n\r\n @if (pendingScreen()?.label) {\r\n <p class=\"fb-dbg__screen-label\">{{ pendingScreen()?.label }}</p>\r\n }\r\n @if (pendingScreen()?.helpText) {\r\n <p class=\"fb-field__hint\">{{ pendingScreen()?.helpText }}</p>\r\n }\r\n\r\n @if (screenInputRows().length) {\r\n <table class=\"fb-dbg__table\">\r\n <caption>\r\n Valori in ingresso\r\n </caption>\r\n <tbody>\r\n @for (row of screenInputRows(); track row.name) {\r\n <tr>\r\n <th scope=\"row\">{{ row.name }}</th>\r\n <td class=\"fb-dbg__type\">{{ row.type }}</td>\r\n <td>{{ row.value }}</td>\r\n </tr>\r\n }\r\n </tbody>\r\n </table>\r\n }\r\n\r\n @for (output of screenOutputNames(); track output) {\r\n <div class=\"fb-field\">\r\n <label class=\"fb-field__label\">{{ output }}</label>\r\n <input class=\"fb-input\" (input)=\"setScreenOutput(output, $any($event.target).value)\" />\r\n </div>\r\n }\r\n @if (!screenOutputNames().length) {\r\n <p class=\"fb-field__hint\">Lo screen non dichiara parametri di uscita.</p>\r\n }\r\n\r\n <div class=\"fb-field__row\">\r\n <button\r\n type=\"button\"\r\n class=\"fb-btn fb-btn--primary\"\r\n [disabled]=\"isRunning()\"\r\n (click)=\"respond('Next')\"\r\n >\r\n Avanti\r\n </button>\r\n <!-- canGoBack/canFinish/canPause sono la verita', piu' precisa dei flag del metadata. -->\r\n <button\r\n type=\"button\"\r\n class=\"fb-btn\"\r\n [disabled]=\"isRunning() || !canGoBack()\"\r\n title=\"Con \u00ABindietro\u00BB gli output non vengono memorizzati\"\r\n (click)=\"respond('Previous')\"\r\n >\r\n Indietro\r\n </button>\r\n <button type=\"button\" class=\"fb-btn\" [disabled]=\"isRunning() || !canFinish()\" (click)=\"respond('Finish')\">\r\n Fine\r\n </button>\r\n <button\r\n type=\"button\"\r\n class=\"fb-btn\"\r\n [disabled]=\"isRunning() || !canPause()\"\r\n title=\"Con \u00ABpausa\u00BB gli output non vengono memorizzati\"\r\n (click)=\"respond('Pause')\"\r\n >\r\n Pausa\r\n </button>\r\n </div>\r\n <p class=\"fb-field__hint\">\r\n Con \u00ABindietro\u00BB e \u00ABpausa\u00BB i valori inseriti <strong>non</strong> vengono memorizzati.\r\n </p>\r\n </fieldset>\r\n }\r\n\r\n @if (isWaitingForStageStep()) {\r\n <fieldset class=\"fb-section\">\r\n <legend class=\"fb-section__title\">Step di orchestrazione</legend>\r\n <p class=\"fb-section__note\">\r\n L\u2019interview e\u2019 sospesa su uno step assegnato: qui si conclude al posto dell\u2019assegnatario.\r\n Concludere puo\u2019 far <strong>sospendere di nuovo</strong> lo stage, con una chiave nuova.\r\n </p>\r\n\r\n <table class=\"fb-dbg__table\">\r\n <tbody>\r\n @for (step of stageSteps(); track step.stepName) {\r\n <tr>\r\n <th scope=\"row\">{{ step.label || step.stepName }}</th>\r\n <td class=\"fb-dbg__type\">{{ step.actionType }}</td>\r\n <td>{{ step.isWaiting ? 'in attesa' : step.status }}</td>\r\n </tr>\r\n }\r\n </tbody>\r\n </table>\r\n\r\n @for (step of waitingStageSteps(); track step.stepName) {\r\n <div class=\"fb-list__item\">\r\n <div class=\"fb-list__header\">\r\n <span class=\"fb-list__title\">{{ step.label || step.stepName }}</span>\r\n </div>\r\n @for (output of stepOutputNames(step.stepName); track output) {\r\n <div class=\"fb-field\">\r\n <label class=\"fb-field__label\">{{ output }}</label>\r\n <input class=\"fb-input\" (input)=\"setStepOutput(output, $any($event.target).value)\" />\r\n </div>\r\n }\r\n <div class=\"fb-field__row\">\r\n <button\r\n type=\"button\"\r\n class=\"fb-btn fb-btn--primary\"\r\n [disabled]=\"isRunning()\"\r\n (click)=\"completeStep(step.stepName, 'Completed')\"\r\n >\r\n Concludi\r\n </button>\r\n <!-- Il rifiuto non e' un errore: prende il ramo \u00ABStep rifiutato\u00BB dello stage. -->\r\n <button\r\n type=\"button\"\r\n class=\"fb-btn\"\r\n [disabled]=\"isRunning()\"\r\n title=\"Prende il ramo \u00ABStep rifiutato\u00BB; senza quel ramo l\u2019interview fallisce\"\r\n (click)=\"completeStep(step.stepName, 'Rejected')\"\r\n >\r\n Rifiuta\r\n </button>\r\n <button\r\n type=\"button\"\r\n class=\"fb-btn fb-btn--ghost\"\r\n [disabled]=\"isRunning()\"\r\n (click)=\"completeStep(step.stepName, 'Cancelled')\"\r\n >\r\n Annulla lo step\r\n </button>\r\n </div>\r\n </div>\r\n }\r\n </fieldset>\r\n }\r\n\r\n @if (outputRows().length) {\r\n <fieldset class=\"fb-section\">\r\n <legend class=\"fb-section__title\">Output del flow</legend>\r\n <table class=\"fb-dbg__table\">\r\n <tbody>\r\n @for (row of outputRows(); track row.name) {\r\n <tr>\r\n <th scope=\"row\">{{ row.name }}</th>\r\n <td class=\"fb-dbg__type\">{{ row.type }}</td>\r\n <td>{{ row.value }}</td>\r\n </tr>\r\n }\r\n </tbody>\r\n </table>\r\n </fieldset>\r\n }\r\n\r\n @if (trace().length) {\r\n <fieldset class=\"fb-section\">\r\n <legend class=\"fb-section__title\">Traccia</legend>\r\n <ol class=\"fb-dbg__trace\">\r\n @for (entry of trace(); track entry.sequence) {\r\n <li class=\"fb-dbg__trace-item\">\r\n <span class=\"fb-dbg__trace-seq\">{{ entry.sequence }}</span>\r\n @if (entry.elementName) {\r\n <button type=\"button\" class=\"fb-dbg__trace-el\" (click)=\"elementFocused.emit(entry.elementName!)\">\r\n {{ entry.elementName }}\r\n </button>\r\n }\r\n <span class=\"fb-dbg__trace-msg\">{{ entry.message }}</span>\r\n </li>\r\n }\r\n </ol>\r\n </fieldset>\r\n }\r\n\r\n @if (resourceRows().length) {\r\n <fieldset class=\"fb-section\">\r\n <legend class=\"fb-section__title\">Risorse</legend>\r\n <table class=\"fb-dbg__table\">\r\n <tbody>\r\n @for (row of resourceRows(); track row.name) {\r\n <tr>\r\n <th scope=\"row\">{{ row.name }}</th>\r\n <td class=\"fb-dbg__type\">{{ row.type }}</td>\r\n <td>{{ row.value }}</td>\r\n </tr>\r\n }\r\n </tbody>\r\n </table>\r\n </fieldset>\r\n }\r\n\r\n @if (result()?.interviewKey) {\r\n <p class=\"fb-field__hint\">\r\n Chiave dell\u2019esecuzione sospesa: <code>{{ result()?.interviewKey }}</code>\r\n </p>\r\n }\r\n }\r\n</div>\r\n", styles: [":host{display:flex;flex-direction:column;height:100%;min-height:0;background:var(--fb-surface, #fff)}.fb-dbg__header{display:flex;align-items:center;justify-content:space-between;padding:10px 12px;border-bottom:1px solid var(--fb-border-subtle, #e6e9ee)}.fb-dbg__title{margin:0;font-size:14px;font-weight:600;color:var(--fb-text, #1d2939)}.fb-dbg__body{flex:1;min-height:0;overflow-y:auto;padding:10px 12px}.fb-dbg__status{display:flex;align-items:center;gap:8px;margin-bottom:10px}.fb-dbg__badge{padding:2px 8px;border-radius:10px;background:var(--fb-border, #d6dae1);font-size:11px;font-weight:600;color:var(--fb-text, #1d2939)}.fb-dbg__badge--ok{background:color-mix(in srgb,var(--fb-success, #3f8f5f) 18%,transparent);color:var(--fb-success, #3f8f5f)}.fb-dbg__badge--fail{background:color-mix(in srgb,var(--fb-error, #c9372c) 14%,transparent);color:var(--fb-error, #c9372c)}.fb-dbg__badge--wait{background:color-mix(in srgb,var(--fb-accent, #2f6feb) 12%,transparent);color:var(--fb-accent, #2f6feb)}.fb-dbg__current,.fb-dbg__steps{font-size:10px;color:var(--fb-text-muted, #667085)}.fb-dbg__type{font-family:ui-monospace,SFMono-Regular,Menlo,monospace;font-size:9px;color:var(--fb-text-subtle, #98a2b3)}.fb-dbg__screen-label{margin:0 0 4px;font-size:12px;font-weight:600;color:var(--fb-text, #1d2939)}.fb-dbg__table{width:100%;border-collapse:collapse;font-size:11px}.fb-dbg__table caption{padding-bottom:3px;font-size:10px;color:var(--fb-text-subtle, #98a2b3);text-align:left}.fb-dbg__table th,.fb-dbg__table td{padding:3px 5px;border-bottom:1px solid var(--fb-border-subtle, #e6e9ee);text-align:left;vertical-align:top}.fb-dbg__table th{font-family:ui-monospace,SFMono-Regular,Menlo,monospace;font-weight:600;color:var(--fb-text, #1d2939)}.fb-dbg__table td{color:var(--fb-text-muted, #667085);word-break:break-word}.fb-dbg__trace{margin:0;padding:0;list-style:none}.fb-dbg__trace-item{display:flex;gap:6px;padding:3px 0;border-bottom:1px solid var(--fb-border-subtle, #e6e9ee);font-size:11px}.fb-dbg__trace-seq{flex:0 0 auto;width:18px;font-family:ui-monospace,SFMono-Regular,Menlo,monospace;font-size:9px;color:var(--fb-text-subtle, #98a2b3);text-align:right}.fb-dbg__trace-el{flex:0 0 auto;padding:0;border:0;background:transparent;color:var(--fb-accent, #2f6feb);font:inherit;font-size:10px;cursor:pointer;text-decoration:underline}.fb-dbg__trace-msg{color:var(--fb-text-muted, #667085);line-height:1.35}\n"] }]
6720
7529
  }], propDecorators: { closed: [{ type: i0.Output, args: ["closed"] }], elementFocused: [{ type: i0.Output, args: ["elementFocused"] }] } });
6721
7530
 
6722
7531
  /**
@@ -6821,8 +7630,15 @@ class FlowBuilderComponent {
6821
7630
  }
6822
7631
  }, ...(ngDevMode ? [{ debugName: "statusLabel" }] : []));
6823
7632
  constructor() {
6824
- // I dizionari sono statici: una volta sola, in cache (§6.4).
6825
- void this.dictionaries.load();
7633
+ /**
7634
+ * I dizionari si caricano una volta e si tengono in cache, **per `processType`**: le
7635
+ * globali dipendono dal tipo di flow — `$Record` non esiste in uno screen flow — e
7636
+ * cambiare tipo cambia quali sono proponibili (§4.1, §6.4). Lo store memoizza per tipo,
7637
+ * quindi tornare a un tipo già visto non produce una richiesta.
7638
+ */
7639
+ effect(() => {
7640
+ void this.dictionaries.load(this.store.document().processType);
7641
+ });
6826
7642
  effect(() => {
6827
7643
  this.session.setAuthor(this.author());
6828
7644
  });
@@ -6925,8 +7741,14 @@ class FlowBuilderComponent {
6925
7741
  (this.store.document().screens?.length ?? 0) > 0, ...(ngDevMode ? [{ debugName: "hasScreensInAutoLaunched" }] : []));
6926
7742
  /** Un flow `Screen` senza screen e' `SCREEN_FLOW_WITHOUT_SCREENS`. */
6927
7743
  hasNoScreensInScreenFlow = computed(() => this.store.document().processType === 'Screen' && (this.store.document().screens?.length ?? 0) === 0, ...(ngDevMode ? [{ debugName: "hasNoScreensInScreenFlow" }] : []));
6928
- /** `Orchestration` non e' eseguibile dal motore. */
6929
- isOrchestration = computed(() => this.store.document().processType === 'Orchestration', ...(ngDevMode ? [{ debugName: "isOrchestration" }] : []));
7744
+ /**
7745
+ * Un flow `Orchestration` senza stage di orchestrazione e' un **avviso**
7746
+ * (`ORCHESTRATION_WITHOUT_STAGES`): il tipo dichiara un'orchestrazione che non c'e' (§3.1).
7747
+ */
7748
+ isOrchestrationWithoutStages = computed(() => {
7749
+ const document = this.store.document();
7750
+ return document.processType === 'Orchestration' && (document.orchestratedStages?.length ?? 0) === 0;
7751
+ }, ...(ngDevMode ? [{ debugName: "isOrchestrationWithoutStages" }] : []));
6930
7752
  // -------------------------------------------------------------------------
6931
7753
  // Canvas
6932
7754
  // -------------------------------------------------------------------------
@@ -7019,6 +7841,13 @@ class FlowBuilderComponent {
7019
7841
  if (type === 'Wait') {
7020
7842
  node['waitEvents'] = [{ name: 'Evento', label: 'Evento 1' }];
7021
7843
  }
7844
+ if (type === 'OrchestratedStage') {
7845
+ // Lo step nasce senza `actionType`: e' l'utente a scegliere se e' in background,
7846
+ // interattivo o di approvazione, e il form lo segnala finche' manca (§5.13).
7847
+ node['stageSteps'] = [
7848
+ { name: uniqueFlowName('Step', [...this.store.usedNames(), name]), label: 'Step 1' },
7849
+ ];
7850
+ }
7022
7851
  this.store.addNode(collection, node);
7023
7852
  // Un elemento appena creato e' vuoto: il posto giusto in cui finire e' il suo form.
7024
7853
  this.openElement(name);
@@ -7241,7 +8070,7 @@ class FlowBuilderComponent {
7241
8070
  FlowValidationStore,
7242
8071
  FlowEditorSession,
7243
8072
  FlowLayoutService,
7244
- ], ngImport: i0, template: "<div class=\"fb-builder\" [attr.data-fb-theme]=\"null\">\r\n <header class=\"fb-top\">\r\n <div class=\"fb-top__identity\">\r\n <input\r\n class=\"fb-top__label\"\r\n [value]=\"document().label || ''\"\r\n placeholder=\"Nome del flow\"\r\n aria-label=\"Nome del flow\"\r\n [disabled]=\"!isEditable()\"\r\n (input)=\"setLabel($any($event.target).value)\"\r\n />\r\n <div class=\"fb-top__meta\">\r\n <input\r\n class=\"fb-top__name\"\r\n [value]=\"document().fullName || ''\"\r\n placeholder=\"NomeTecnico\"\r\n aria-label=\"Nome tecnico del flow\"\r\n [disabled]=\"!isEditable()\"\r\n (input)=\"setFullName($any($event.target).value)\"\r\n />\r\n <select\r\n class=\"fb-top__process\"\r\n [fbValue]=\"document().processType || ''\"\r\n aria-label=\"Tipo di flow\"\r\n [disabled]=\"!isEditable()\"\r\n (change)=\"setProcessType($any($event.target).value)\"\r\n >\r\n @for (type of processTypes(); track type.value) {\r\n <option [value]=\"type.value\">{{ type.label }}</option>\r\n }\r\n </select>\r\n <span class=\"fb-top__status\">{{ statusLabel() }}</span>\r\n @if (session.version() !== null) {\r\n <span class=\"fb-top__version\">v{{ session.version() }}</span>\r\n }\r\n @if (isDirty()) {\r\n <span class=\"fb-top__dirty\" title=\"Ci sono modifiche non salvate\">modificato</span>\r\n }\r\n </div>\r\n </div>\r\n\r\n <div class=\"fb-top__actions\">\r\n <button type=\"button\" class=\"fb-btn\" [disabled]=\"!canUndo()\" aria-label=\"Annulla\" (click)=\"undo()\">\u21B6</button>\r\n <button type=\"button\" class=\"fb-btn\" [disabled]=\"!canRedo()\" aria-label=\"Ripeti\" (click)=\"redo()\">\u21B7</button>\r\n <button type=\"button\" class=\"fb-btn\" title=\"Ricalcola le posizioni\" (click)=\"autoLayout()\">Riordina</button>\r\n @if (isDialogMode()) {\r\n <!-- Il doppio click sul node fa la stessa cosa, ma non si vede: questo comando s\u00EC. -->\r\n <button\r\n type=\"button\"\r\n class=\"fb-btn\"\r\n [disabled]=\"!selectedName()\"\r\n title=\"Apri il dettaglio dell\u2019elemento selezionato\"\r\n (click)=\"openSelectedElement()\"\r\n >\r\n Dettaglio\r\n </button>\r\n }\r\n <button type=\"button\" class=\"fb-btn\" [disabled]=\"isBusy()\" (click)=\"validateNow()\">Valida</button>\r\n\r\n @switch (primaryCommand()) {\r\n @case ('newVersion') {\r\n <!-- Su una versione non modificabile il comando primario e' \"Nuova versione\" (\u00A78.1). -->\r\n <button type=\"button\" class=\"fb-btn fb-btn--primary\" [disabled]=\"isBusy()\" (click)=\"createNewVersion()\">\r\n Nuova versione\r\n </button>\r\n }\r\n @case ('create') {\r\n <button type=\"button\" class=\"fb-btn fb-btn--primary\" [disabled]=\"isBusy()\" (click)=\"save()\">\r\n Crea\r\n </button>\r\n }\r\n @default {\r\n <button type=\"button\" class=\"fb-btn fb-btn--primary\" [disabled]=\"isBusy()\" (click)=\"save()\">\r\n Salva\r\n </button>\r\n }\r\n }\r\n\r\n <button\r\n type=\"button\"\r\n class=\"fb-btn\"\r\n [disabled]=\"isBusy() || !canActivate()\"\r\n [title]=\"\r\n canActivate()\r\n ? 'Attiva questa versione'\r\n : 'L\u2019attivazione esige zero errori: correggili nel pannello dei problemi'\r\n \"\r\n (click)=\"activate()\"\r\n >\r\n Attiva\r\n </button>\r\n </div>\r\n </header>\r\n\r\n @if (conflict()) {\r\n <!-- \u00A79.3: qualcun altro ha salvato. Due strade, entrambe offerte. -->\r\n <div class=\"fb-banner fb-banner--warn\" role=\"alert\">\r\n <span>\r\n {{ conflict()?.message }}\r\n @if (conflict()?.conflictingAuthor) {\r\n Ha salvato {{ conflict()?.conflictingAuthor }}.\r\n }\r\n </span>\r\n <button type=\"button\" class=\"fb-btn fb-btn--icon\" (click)=\"reloadAfterConflict()\">Ricarica</button>\r\n <button type=\"button\" class=\"fb-btn fb-btn--icon\" (click)=\"saveAsNewVersionAfterConflict()\">\r\n Salva come nuova versione\r\n </button>\r\n <button type=\"button\" class=\"fb-btn fb-btn--ghost fb-btn--icon\" (click)=\"dismissConflict()\">\u00D7</button>\r\n </div>\r\n }\r\n\r\n @if (notice()) {\r\n <div\r\n class=\"fb-banner\"\r\n [class.fb-banner--error]=\"notice()?.kind === 'error'\"\r\n role=\"status\"\r\n >\r\n <span>{{ notice()?.message }}</span>\r\n <button type=\"button\" class=\"fb-btn fb-btn--ghost fb-btn--icon\" aria-label=\"Chiudi\" (click)=\"dismissNotice()\">\r\n \u00D7\r\n </button>\r\n </div>\r\n }\r\n\r\n @if (hasScreensInAutoLaunched()) {\r\n <div class=\"fb-banner fb-banner--error\">\r\n Un flow AutoLaunched che contiene screen e\u2019 un errore di validazione: non c\u2019e\u2019 nessuno a cui mostrarli.\r\n </div>\r\n }\r\n @if (isOrchestration()) {\r\n <div class=\"fb-banner fb-banner--warn\">Un flow Orchestration non e\u2019 eseguibile dal motore.</div>\r\n }\r\n @if (hasNoScreensInScreenFlow()) {\r\n <div class=\"fb-banner fb-banner--warn\">\r\n Un flow di tipo Screen senza nessuno screen viene segnalato dalla validazione.\r\n </div>\r\n }\r\n @if (!isEditable()) {\r\n <div class=\"fb-banner\">\r\n Questa versione e\u2019 in sola lettura: per modificarla creane una nuova.\r\n </div>\r\n }\r\n\r\n <div class=\"fb-main\">\r\n <aside class=\"fb-main__palette\">\r\n <fb-element-palette [processType]=\"document().processType\" (elementPicked)=\"onElementPicked($event)\" />\r\n </aside>\r\n\r\n <div class=\"fb-main__center\">\r\n <fb-flow-canvas\r\n class=\"fb-main__canvas\"\r\n [selectedName]=\"selectedName()\"\r\n [outline]=\"outline()\"\r\n (selectionChange)=\"onSelectionChange($event)\"\r\n (nodeOpened)=\"onNodeOpened($event)\"\r\n (nodeRemoveRequested)=\"onRemoveNode($event)\"\r\n (nodeDuplicateRequested)=\"onDuplicateNode($event)\"\r\n (elementDropped)=\"onElementDropped($event)\"\r\n />\r\n\r\n @if (showProblems()) {\r\n <fb-problems-panel\r\n class=\"fb-main__problems\"\r\n (elementFocused)=\"focusElement($event)\"\r\n (closed)=\"toggleProblems()\"\r\n />\r\n } @else {\r\n <button type=\"button\" class=\"fb-main__problems-toggle\" (click)=\"toggleProblems()\">\r\n Problemi\r\n @if (errorCount()) {\r\n <span class=\"fb-main__count fb-main__count--error\">{{ errorCount() }}</span>\r\n }\r\n @if (warningCount()) {\r\n <span class=\"fb-main__count fb-main__count--warn\">{{ warningCount() }}</span>\r\n }\r\n </button>\r\n }\r\n </div>\r\n\r\n <aside class=\"fb-main__side\">\r\n <nav class=\"fb-side__tabs\" aria-label=\"Pannelli\">\r\n @if (!isDialogMode()) {\r\n <button\r\n type=\"button\"\r\n class=\"fb-side__tab\"\r\n [class.fb-side__tab--active]=\"sidePanel() === 'inspector'\"\r\n (click)=\"setPanel('inspector')\"\r\n >\r\n Elemento\r\n </button>\r\n }\r\n <button\r\n type=\"button\"\r\n class=\"fb-side__tab\"\r\n [class.fb-side__tab--active]=\"sidePanel() === 'resources'\"\r\n (click)=\"setPanel('resources')\"\r\n >\r\n Risorse\r\n </button>\r\n <button\r\n type=\"button\"\r\n class=\"fb-side__tab\"\r\n [class.fb-side__tab--active]=\"sidePanel() === 'versions'\"\r\n (click)=\"setPanel('versions')\"\r\n >\r\n Versioni\r\n </button>\r\n <button\r\n type=\"button\"\r\n class=\"fb-side__tab\"\r\n [class.fb-side__tab--active]=\"sidePanel() === 'debug'\"\r\n (click)=\"setPanel('debug')\"\r\n >\r\n Prova\r\n </button>\r\n </nav>\r\n\r\n <div class=\"fb-side__content\">\r\n @switch (sidePanel()) {\r\n @case ('inspector') {\r\n <fb-element-inspector\r\n [selectedName]=\"selectedName()\"\r\n (removeRequested)=\"onRemoveNode($event)\"\r\n (duplicateRequested)=\"onDuplicateNode($event)\"\r\n (closed)=\"setPanel('resources')\"\r\n />\r\n }\r\n @case ('resources') {\r\n <fb-resource-panel (closed)=\"setPanel('inspector')\" />\r\n }\r\n @case ('versions') {\r\n <fb-version-panel\r\n (versionOpened)=\"openVersion($event)\"\r\n (notice)=\"showNotice($event)\"\r\n (closed)=\"setPanel('inspector')\"\r\n />\r\n }\r\n @case ('debug') {\r\n <!-- Evidenzia sul canvas senza rubare il pannello: l'interview e\u2019 in corso. -->\r\n <fb-debug-panel (elementFocused)=\"highlightElement($event)\" (closed)=\"setPanel('inspector')\" />\r\n }\r\n }\r\n </div>\r\n\r\n <footer class=\"fb-side__footer\">\r\n <label class=\"fb-btn fb-btn--icon\">\r\n Importa JSON\r\n <input type=\"file\" accept=\"application/json,.json\" hidden (change)=\"onFileSelected($event)\" />\r\n </label>\r\n </footer>\r\n </aside>\r\n </div>\r\n\r\n @if (isDialogMode() && isDialogOpen() && selectedName()) {\r\n <!-- La dialog sta dentro il builder, non nel body: la libreria e\u2019 innestabile. -->\r\n <fb-element-dialog\r\n [selectedName]=\"selectedName()\"\r\n (closed)=\"closeDialog()\"\r\n (removeRequested)=\"onRemoveNode($event)\"\r\n (duplicateRequested)=\"onDuplicateNode($event)\"\r\n />\r\n }\r\n</div>\r\n", styles: [":host{display:block;width:100%;height:100%;min-height:0;font:inherit;color:var(--fb-text, #1d2939)}.fb-builder{position:relative;display:flex;flex-direction:column;width:100%;height:100%;min-height:0;background:var(--fb-surface, #fff)}.fb-top{display:flex;align-items:center;justify-content:space-between;gap:12px;padding:9px 14px;border-bottom:1px solid var(--fb-border, #e2e5eb);background:var(--fb-surface, #fff)}.fb-top__identity{min-width:0}.fb-top__label{width:100%;max-width:420px;padding:2px 4px;border:1px solid transparent;border-radius:4px;background:transparent;color:var(--fb-text, #1d2939);font:inherit;font-size:15px;font-weight:600}.fb-top__label:hover:not(:disabled),.fb-top__label:focus-visible{border-color:var(--fb-border, #d6dae1);background:var(--fb-surface, #fff)}.fb-top__meta{display:flex;flex-wrap:wrap;align-items:center;gap:6px;margin-top:2px}.fb-top__name{width:180px;padding:1px 4px;border:1px solid transparent;border-radius:4px;background:transparent;color:var(--fb-text-muted, #667085);font-family:ui-monospace,SFMono-Regular,Menlo,monospace;font-size:11px}.fb-top__name:hover:not(:disabled),.fb-top__name:focus-visible{border-color:var(--fb-border, #d6dae1)}.fb-top__process{padding:1px 4px;border:1px solid var(--fb-border-subtle, #e6e9ee);border-radius:4px;background:transparent;color:var(--fb-text-muted, #667085);font:inherit;font-size:11px}.fb-top__status,.fb-top__version,.fb-top__dirty{padding:2px 7px;border-radius:999px;background:var(--fb-surface-sunken, #eef0f4);color:var(--fb-text-muted, #6b7086);font-size:10px;font-weight:600}.fb-top__version{font-family:ui-monospace,SFMono-Regular,Menlo,monospace}.fb-top__dirty{background:color-mix(in srgb,var(--fb-warning, #b7791f) 16%,transparent);color:var(--fb-warning, #b7791f)}.fb-top__actions{display:flex;flex-wrap:wrap;gap:4px}.fb-banner{display:flex;flex-wrap:wrap;align-items:center;gap:8px;padding:6px 12px;border-bottom:1px solid var(--fb-border-subtle, #e6e9ee);background:color-mix(in srgb,var(--fb-accent, #2f6feb) 7%,transparent);font-size:11px}.fb-banner--warn{background:color-mix(in srgb,var(--fb-warning, #b7791f) 12%,transparent);color:var(--fb-warning, #b7791f)}.fb-banner--error{background:color-mix(in srgb,var(--fb-error, #c9372c) 10%,transparent);color:var(--fb-error, #c9372c)}.fb-banner>span{flex:1;min-width:200px}.fb-main{display:flex;flex:1;min-height:0}.fb-main__palette{flex:0 0 190px;min-width:0}.fb-main__center{display:flex;flex:1;flex-direction:column;min-width:0;min-height:0}.fb-main__canvas{flex:1;min-height:0}.fb-main__problems{flex:0 0 auto;height:220px}.fb-main__problems-toggle{display:flex;align-items:center;gap:6px;padding:4px 12px;border:0;border-top:1px solid var(--fb-border, #d6dae1);background:var(--fb-surface-alt, #f8f9fb);color:var(--fb-text-muted, #667085);font:inherit;font-size:11px;cursor:pointer}.fb-main__count{padding:0 5px;border-radius:8px;background:var(--fb-border, #d6dae1);font-size:9px;font-weight:700}.fb-main__count--error{background:color-mix(in srgb,var(--fb-error, #c9372c) 16%,transparent);color:var(--fb-error, #c9372c)}.fb-main__count--warn{background:color-mix(in srgb,var(--fb-warning, #b7791f) 16%,transparent);color:var(--fb-warning, #b7791f)}.fb-main__side{display:flex;flex-direction:column;flex:0 0 340px;min-width:0;min-height:0;border-left:1px solid var(--fb-border, #d6dae1);background:var(--fb-surface, #fff)}.fb-side__tabs{display:flex;gap:2px;margin:8px 10px;padding:3px;border-radius:var(--fb-radius, 10px);background:var(--fb-surface-sunken, #eef0f4)}.fb-side__tab{flex:1;padding:5px 8px;border:0;border-radius:var(--fb-radius-xs, 6px);background:transparent;color:var(--fb-text-muted, #6b7086);font:inherit;font-size:11px;cursor:pointer;transition:background .12s ease,color .12s ease}.fb-side__tab:hover:not(.fb-side__tab--active){color:var(--fb-text, #1a1c23)}.fb-side__tab--active{background:var(--fb-surface, #fff);box-shadow:var(--fb-shadow-sm, 0 1px 2px rgb(16 24 40 / 6%));color:var(--fb-text, #1a1c23);font-weight:600}.fb-side__content{flex:1;min-height:0;border-top:1px solid var(--fb-border-subtle, #eef0f4)}.fb-side__content>*{height:100%}.fb-side__footer{display:flex;gap:6px;padding:6px 8px;border-top:1px solid var(--fb-border-subtle, #e6e9ee)}@media(max-width:1200px){.fb-main__palette{flex-basis:150px}.fb-main__side{flex-basis:290px}}\n"], dependencies: [{ kind: "component", type: DebugPanelComponent, selector: "fb-debug-panel", outputs: ["closed", "elementFocused"] }, { kind: "component", type: ElementDialogComponent, selector: "fb-element-dialog", inputs: ["selectedName"], outputs: ["closed", "removeRequested", "duplicateRequested"] }, { kind: "component", type: ElementInspectorComponent, selector: "fb-element-inspector", inputs: ["selectedName", "showHeader"], outputs: ["closed", "removeRequested", "duplicateRequested"] }, { kind: "component", type: ElementPaletteComponent, selector: "fb-element-palette", inputs: ["processType"], outputs: ["elementPicked"] }, { kind: "component", type: FlowCanvasComponent, selector: "fb-flow-canvas", inputs: ["selectedName", "outline"], outputs: ["selectionChange", "nodeOpened", "nodeRemoveRequested", "nodeDuplicateRequested", "elementDropped"] }, { kind: "component", type: ProblemsPanelComponent, selector: "fb-problems-panel", outputs: ["elementFocused", "closed"] }, { kind: "component", type: ResourcePanelComponent, selector: "fb-resource-panel", outputs: ["closed"] }, { kind: "component", type: VersionPanelComponent, selector: "fb-version-panel", outputs: ["closed", "versionOpened", "notice"] }, { kind: "directive", type: SelectValueDirective, selector: "select[fbValue]", inputs: ["fbValue"] }], changeDetection: i0.ChangeDetectionStrategy.OnPush });
8073
+ ], ngImport: i0, template: "<div class=\"fb-builder\" [attr.data-fb-theme]=\"null\">\r\n <header class=\"fb-top\">\r\n <div class=\"fb-top__identity\">\r\n <input\r\n class=\"fb-top__label\"\r\n [value]=\"document().label || ''\"\r\n placeholder=\"Nome del flow\"\r\n aria-label=\"Nome del flow\"\r\n [disabled]=\"!isEditable()\"\r\n (input)=\"setLabel($any($event.target).value)\"\r\n />\r\n <div class=\"fb-top__meta\">\r\n <input\r\n class=\"fb-top__name\"\r\n [value]=\"document().fullName || ''\"\r\n placeholder=\"NomeTecnico\"\r\n aria-label=\"Nome tecnico del flow\"\r\n [disabled]=\"!isEditable()\"\r\n (input)=\"setFullName($any($event.target).value)\"\r\n />\r\n <select\r\n class=\"fb-top__process\"\r\n [fbValue]=\"document().processType || ''\"\r\n aria-label=\"Tipo di flow\"\r\n [disabled]=\"!isEditable()\"\r\n (change)=\"setProcessType($any($event.target).value)\"\r\n >\r\n @for (type of processTypes(); track type.value) {\r\n <option [value]=\"type.value\">{{ type.label }}</option>\r\n }\r\n </select>\r\n <span class=\"fb-top__status\">{{ statusLabel() }}</span>\r\n @if (session.version() !== null) {\r\n <span class=\"fb-top__version\">v{{ session.version() }}</span>\r\n }\r\n @if (isDirty()) {\r\n <span class=\"fb-top__dirty\" title=\"Ci sono modifiche non salvate\">modificato</span>\r\n }\r\n </div>\r\n </div>\r\n\r\n <div class=\"fb-top__actions\">\r\n <button type=\"button\" class=\"fb-btn\" [disabled]=\"!canUndo()\" aria-label=\"Annulla\" (click)=\"undo()\">\u21B6</button>\r\n <button type=\"button\" class=\"fb-btn\" [disabled]=\"!canRedo()\" aria-label=\"Ripeti\" (click)=\"redo()\">\u21B7</button>\r\n <button type=\"button\" class=\"fb-btn\" title=\"Ricalcola le posizioni\" (click)=\"autoLayout()\">Riordina</button>\r\n @if (isDialogMode()) {\r\n <!-- Il doppio click sul node fa la stessa cosa, ma non si vede: questo comando s\u00EC. -->\r\n <button\r\n type=\"button\"\r\n class=\"fb-btn\"\r\n [disabled]=\"!selectedName()\"\r\n title=\"Apri il dettaglio dell\u2019elemento selezionato\"\r\n (click)=\"openSelectedElement()\"\r\n >\r\n Dettaglio\r\n </button>\r\n }\r\n <button type=\"button\" class=\"fb-btn\" [disabled]=\"isBusy()\" (click)=\"validateNow()\">Valida</button>\r\n\r\n @switch (primaryCommand()) {\r\n @case ('newVersion') {\r\n <!-- Su una versione non modificabile il comando primario e' \"Nuova versione\" (\u00A78.1). -->\r\n <button type=\"button\" class=\"fb-btn fb-btn--primary\" [disabled]=\"isBusy()\" (click)=\"createNewVersion()\">\r\n Nuova versione\r\n </button>\r\n }\r\n @case ('create') {\r\n <button type=\"button\" class=\"fb-btn fb-btn--primary\" [disabled]=\"isBusy()\" (click)=\"save()\">\r\n Crea\r\n </button>\r\n }\r\n @default {\r\n <button type=\"button\" class=\"fb-btn fb-btn--primary\" [disabled]=\"isBusy()\" (click)=\"save()\">\r\n Salva\r\n </button>\r\n }\r\n }\r\n\r\n <button\r\n type=\"button\"\r\n class=\"fb-btn\"\r\n [disabled]=\"isBusy() || !canActivate()\"\r\n [title]=\"\r\n canActivate()\r\n ? 'Attiva questa versione'\r\n : 'L\u2019attivazione esige zero errori: correggili nel pannello dei problemi'\r\n \"\r\n (click)=\"activate()\"\r\n >\r\n Attiva\r\n </button>\r\n </div>\r\n </header>\r\n\r\n @if (conflict()) {\r\n <!-- \u00A79.3: qualcun altro ha salvato. Due strade, entrambe offerte. -->\r\n <div class=\"fb-banner fb-banner--warn\" role=\"alert\">\r\n <span>\r\n {{ conflict()?.message }}\r\n @if (conflict()?.conflictingAuthor) {\r\n Ha salvato {{ conflict()?.conflictingAuthor }}.\r\n }\r\n </span>\r\n <button type=\"button\" class=\"fb-btn fb-btn--icon\" (click)=\"reloadAfterConflict()\">Ricarica</button>\r\n <button type=\"button\" class=\"fb-btn fb-btn--icon\" (click)=\"saveAsNewVersionAfterConflict()\">\r\n Salva come nuova versione\r\n </button>\r\n <button type=\"button\" class=\"fb-btn fb-btn--ghost fb-btn--icon\" (click)=\"dismissConflict()\">\u00D7</button>\r\n </div>\r\n }\r\n\r\n @if (notice()) {\r\n <div\r\n class=\"fb-banner\"\r\n [class.fb-banner--error]=\"notice()?.kind === 'error'\"\r\n role=\"status\"\r\n >\r\n <span>{{ notice()?.message }}</span>\r\n <button type=\"button\" class=\"fb-btn fb-btn--ghost fb-btn--icon\" aria-label=\"Chiudi\" (click)=\"dismissNotice()\">\r\n \u00D7\r\n </button>\r\n </div>\r\n }\r\n\r\n @if (hasScreensInAutoLaunched()) {\r\n <div class=\"fb-banner fb-banner--error\">\r\n Un flow AutoLaunched che contiene screen e\u2019 un errore di validazione: non c\u2019e\u2019 nessuno a cui mostrarli.\r\n </div>\r\n }\r\n @if (isOrchestrationWithoutStages()) {\r\n <div class=\"fb-banner fb-banner--warn\">\r\n Un flow Orchestration senza stage di orchestrazione viene segnalato dalla validazione\r\n (ORCHESTRATION_WITHOUT_STAGES).\r\n </div>\r\n }\r\n @if (hasNoScreensInScreenFlow()) {\r\n <div class=\"fb-banner fb-banner--warn\">\r\n Un flow di tipo Screen senza nessuno screen viene segnalato dalla validazione.\r\n </div>\r\n }\r\n @if (!isEditable()) {\r\n <div class=\"fb-banner\">\r\n Questa versione e\u2019 in sola lettura: per modificarla creane una nuova.\r\n </div>\r\n }\r\n\r\n <div class=\"fb-main\">\r\n <aside class=\"fb-main__palette\">\r\n <fb-element-palette [processType]=\"document().processType\" (elementPicked)=\"onElementPicked($event)\" />\r\n </aside>\r\n\r\n <div class=\"fb-main__center\">\r\n <fb-flow-canvas\r\n class=\"fb-main__canvas\"\r\n [selectedName]=\"selectedName()\"\r\n [outline]=\"outline()\"\r\n (selectionChange)=\"onSelectionChange($event)\"\r\n (nodeOpened)=\"onNodeOpened($event)\"\r\n (nodeRemoveRequested)=\"onRemoveNode($event)\"\r\n (nodeDuplicateRequested)=\"onDuplicateNode($event)\"\r\n (elementDropped)=\"onElementDropped($event)\"\r\n />\r\n\r\n @if (showProblems()) {\r\n <fb-problems-panel\r\n class=\"fb-main__problems\"\r\n (elementFocused)=\"focusElement($event)\"\r\n (closed)=\"toggleProblems()\"\r\n />\r\n } @else {\r\n <button type=\"button\" class=\"fb-main__problems-toggle\" (click)=\"toggleProblems()\">\r\n Problemi\r\n @if (errorCount()) {\r\n <span class=\"fb-main__count fb-main__count--error\">{{ errorCount() }}</span>\r\n }\r\n @if (warningCount()) {\r\n <span class=\"fb-main__count fb-main__count--warn\">{{ warningCount() }}</span>\r\n }\r\n </button>\r\n }\r\n </div>\r\n\r\n <aside class=\"fb-main__side\">\r\n <nav class=\"fb-side__tabs\" aria-label=\"Pannelli\">\r\n @if (!isDialogMode()) {\r\n <button\r\n type=\"button\"\r\n class=\"fb-side__tab\"\r\n [class.fb-side__tab--active]=\"sidePanel() === 'inspector'\"\r\n (click)=\"setPanel('inspector')\"\r\n >\r\n Elemento\r\n </button>\r\n }\r\n <button\r\n type=\"button\"\r\n class=\"fb-side__tab\"\r\n [class.fb-side__tab--active]=\"sidePanel() === 'resources'\"\r\n (click)=\"setPanel('resources')\"\r\n >\r\n Risorse\r\n </button>\r\n <button\r\n type=\"button\"\r\n class=\"fb-side__tab\"\r\n [class.fb-side__tab--active]=\"sidePanel() === 'versions'\"\r\n (click)=\"setPanel('versions')\"\r\n >\r\n Versioni\r\n </button>\r\n <button\r\n type=\"button\"\r\n class=\"fb-side__tab\"\r\n [class.fb-side__tab--active]=\"sidePanel() === 'debug'\"\r\n (click)=\"setPanel('debug')\"\r\n >\r\n Prova\r\n </button>\r\n </nav>\r\n\r\n <div class=\"fb-side__content\">\r\n @switch (sidePanel()) {\r\n @case ('inspector') {\r\n <fb-element-inspector\r\n [selectedName]=\"selectedName()\"\r\n (removeRequested)=\"onRemoveNode($event)\"\r\n (duplicateRequested)=\"onDuplicateNode($event)\"\r\n (closed)=\"setPanel('resources')\"\r\n />\r\n }\r\n @case ('resources') {\r\n <fb-resource-panel (closed)=\"setPanel('inspector')\" />\r\n }\r\n @case ('versions') {\r\n <fb-version-panel\r\n (versionOpened)=\"openVersion($event)\"\r\n (notice)=\"showNotice($event)\"\r\n (closed)=\"setPanel('inspector')\"\r\n />\r\n }\r\n @case ('debug') {\r\n <!-- Evidenzia sul canvas senza rubare il pannello: l'interview e\u2019 in corso. -->\r\n <fb-debug-panel (elementFocused)=\"highlightElement($event)\" (closed)=\"setPanel('inspector')\" />\r\n }\r\n }\r\n </div>\r\n\r\n <footer class=\"fb-side__footer\">\r\n <label class=\"fb-btn fb-btn--icon\">\r\n Importa JSON\r\n <input type=\"file\" accept=\"application/json,.json\" hidden (change)=\"onFileSelected($event)\" />\r\n </label>\r\n </footer>\r\n </aside>\r\n </div>\r\n\r\n @if (isDialogMode() && isDialogOpen() && selectedName()) {\r\n <!-- La dialog sta dentro il builder, non nel body: la libreria e\u2019 innestabile. -->\r\n <fb-element-dialog\r\n [selectedName]=\"selectedName()\"\r\n (closed)=\"closeDialog()\"\r\n (removeRequested)=\"onRemoveNode($event)\"\r\n (duplicateRequested)=\"onDuplicateNode($event)\"\r\n />\r\n }\r\n</div>\r\n", styles: [":host{display:block;width:100%;height:100%;min-height:0;font:inherit;color:var(--fb-text, #1d2939)}.fb-builder{position:relative;display:flex;flex-direction:column;width:100%;height:100%;min-height:0;background:var(--fb-surface, #fff)}.fb-top{display:flex;align-items:center;justify-content:space-between;gap:12px;padding:9px 14px;border-bottom:1px solid var(--fb-border, #e2e5eb);background:var(--fb-surface, #fff)}.fb-top__identity{min-width:0}.fb-top__label{width:100%;max-width:420px;padding:2px 4px;border:1px solid transparent;border-radius:4px;background:transparent;color:var(--fb-text, #1d2939);font:inherit;font-size:15px;font-weight:600}.fb-top__label:hover:not(:disabled),.fb-top__label:focus-visible{border-color:var(--fb-border, #d6dae1);background:var(--fb-surface, #fff)}.fb-top__meta{display:flex;flex-wrap:wrap;align-items:center;gap:6px;margin-top:2px}.fb-top__name{width:180px;padding:1px 4px;border:1px solid transparent;border-radius:4px;background:transparent;color:var(--fb-text-muted, #667085);font-family:ui-monospace,SFMono-Regular,Menlo,monospace;font-size:11px}.fb-top__name:hover:not(:disabled),.fb-top__name:focus-visible{border-color:var(--fb-border, #d6dae1)}.fb-top__process{padding:1px 4px;border:1px solid var(--fb-border-subtle, #e6e9ee);border-radius:4px;background:transparent;color:var(--fb-text-muted, #667085);font:inherit;font-size:11px}.fb-top__status,.fb-top__version,.fb-top__dirty{padding:2px 7px;border-radius:999px;background:var(--fb-surface-sunken, #eef0f4);color:var(--fb-text-muted, #6b7086);font-size:10px;font-weight:600}.fb-top__version{font-family:ui-monospace,SFMono-Regular,Menlo,monospace}.fb-top__dirty{background:color-mix(in srgb,var(--fb-warning, #b7791f) 16%,transparent);color:var(--fb-warning, #b7791f)}.fb-top__actions{display:flex;flex-wrap:wrap;gap:4px}.fb-banner{display:flex;flex-wrap:wrap;align-items:center;gap:8px;padding:6px 12px;border-bottom:1px solid var(--fb-border-subtle, #e6e9ee);background:color-mix(in srgb,var(--fb-accent, #2f6feb) 7%,transparent);font-size:11px}.fb-banner--warn{background:color-mix(in srgb,var(--fb-warning, #b7791f) 12%,transparent);color:var(--fb-warning, #b7791f)}.fb-banner--error{background:color-mix(in srgb,var(--fb-error, #c9372c) 10%,transparent);color:var(--fb-error, #c9372c)}.fb-banner>span{flex:1;min-width:200px}.fb-main{display:flex;flex:1;min-height:0}.fb-main__palette{flex:0 0 190px;min-width:0}.fb-main__center{display:flex;flex:1;flex-direction:column;min-width:0;min-height:0}.fb-main__canvas{flex:1;min-height:0}.fb-main__problems{flex:0 0 auto;height:220px}.fb-main__problems-toggle{display:flex;align-items:center;gap:6px;padding:4px 12px;border:0;border-top:1px solid var(--fb-border, #d6dae1);background:var(--fb-surface-alt, #f8f9fb);color:var(--fb-text-muted, #667085);font:inherit;font-size:11px;cursor:pointer}.fb-main__count{padding:0 5px;border-radius:8px;background:var(--fb-border, #d6dae1);font-size:9px;font-weight:700}.fb-main__count--error{background:color-mix(in srgb,var(--fb-error, #c9372c) 16%,transparent);color:var(--fb-error, #c9372c)}.fb-main__count--warn{background:color-mix(in srgb,var(--fb-warning, #b7791f) 16%,transparent);color:var(--fb-warning, #b7791f)}.fb-main__side{display:flex;flex-direction:column;flex:0 0 340px;min-width:0;min-height:0;border-left:1px solid var(--fb-border, #d6dae1);background:var(--fb-surface, #fff)}.fb-side__tabs{display:flex;gap:2px;margin:8px 10px;padding:3px;border-radius:var(--fb-radius, 10px);background:var(--fb-surface-sunken, #eef0f4)}.fb-side__tab{flex:1;padding:5px 8px;border:0;border-radius:var(--fb-radius-xs, 6px);background:transparent;color:var(--fb-text-muted, #6b7086);font:inherit;font-size:11px;cursor:pointer;transition:background .12s ease,color .12s ease}.fb-side__tab:hover:not(.fb-side__tab--active){color:var(--fb-text, #1a1c23)}.fb-side__tab--active{background:var(--fb-surface, #fff);box-shadow:var(--fb-shadow-sm, 0 1px 2px rgb(16 24 40 / 6%));color:var(--fb-text, #1a1c23);font-weight:600}.fb-side__content{flex:1;min-height:0;border-top:1px solid var(--fb-border-subtle, #eef0f4)}.fb-side__content>*{height:100%}.fb-side__footer{display:flex;gap:6px;padding:6px 8px;border-top:1px solid var(--fb-border-subtle, #e6e9ee)}@media(max-width:1200px){.fb-main__palette{flex-basis:150px}.fb-main__side{flex-basis:290px}}\n"], dependencies: [{ kind: "component", type: DebugPanelComponent, selector: "fb-debug-panel", outputs: ["closed", "elementFocused"] }, { kind: "component", type: ElementDialogComponent, selector: "fb-element-dialog", inputs: ["selectedName"], outputs: ["closed", "removeRequested", "duplicateRequested"] }, { kind: "component", type: ElementInspectorComponent, selector: "fb-element-inspector", inputs: ["selectedName", "showHeader"], outputs: ["closed", "removeRequested", "duplicateRequested"] }, { kind: "component", type: ElementPaletteComponent, selector: "fb-element-palette", inputs: ["processType"], outputs: ["elementPicked"] }, { kind: "component", type: FlowCanvasComponent, selector: "fb-flow-canvas", inputs: ["selectedName", "outline"], outputs: ["selectionChange", "nodeOpened", "nodeRemoveRequested", "nodeDuplicateRequested", "elementDropped"] }, { kind: "component", type: ProblemsPanelComponent, selector: "fb-problems-panel", outputs: ["elementFocused", "closed"] }, { kind: "component", type: ResourcePanelComponent, selector: "fb-resource-panel", outputs: ["closed"] }, { kind: "component", type: VersionPanelComponent, selector: "fb-version-panel", outputs: ["closed", "versionOpened", "notice"] }, { kind: "directive", type: SelectValueDirective, selector: "select[fbValue]", inputs: ["fbValue"] }], changeDetection: i0.ChangeDetectionStrategy.OnPush });
7245
8074
  }
7246
8075
  i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.27", ngImport: i0, type: FlowBuilderComponent, decorators: [{
7247
8076
  type: Component,
@@ -7259,12 +8088,12 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.27", ngImpo
7259
8088
  FlowValidationStore,
7260
8089
  FlowEditorSession,
7261
8090
  FlowLayoutService,
7262
- ], changeDetection: ChangeDetectionStrategy.OnPush, template: "<div class=\"fb-builder\" [attr.data-fb-theme]=\"null\">\r\n <header class=\"fb-top\">\r\n <div class=\"fb-top__identity\">\r\n <input\r\n class=\"fb-top__label\"\r\n [value]=\"document().label || ''\"\r\n placeholder=\"Nome del flow\"\r\n aria-label=\"Nome del flow\"\r\n [disabled]=\"!isEditable()\"\r\n (input)=\"setLabel($any($event.target).value)\"\r\n />\r\n <div class=\"fb-top__meta\">\r\n <input\r\n class=\"fb-top__name\"\r\n [value]=\"document().fullName || ''\"\r\n placeholder=\"NomeTecnico\"\r\n aria-label=\"Nome tecnico del flow\"\r\n [disabled]=\"!isEditable()\"\r\n (input)=\"setFullName($any($event.target).value)\"\r\n />\r\n <select\r\n class=\"fb-top__process\"\r\n [fbValue]=\"document().processType || ''\"\r\n aria-label=\"Tipo di flow\"\r\n [disabled]=\"!isEditable()\"\r\n (change)=\"setProcessType($any($event.target).value)\"\r\n >\r\n @for (type of processTypes(); track type.value) {\r\n <option [value]=\"type.value\">{{ type.label }}</option>\r\n }\r\n </select>\r\n <span class=\"fb-top__status\">{{ statusLabel() }}</span>\r\n @if (session.version() !== null) {\r\n <span class=\"fb-top__version\">v{{ session.version() }}</span>\r\n }\r\n @if (isDirty()) {\r\n <span class=\"fb-top__dirty\" title=\"Ci sono modifiche non salvate\">modificato</span>\r\n }\r\n </div>\r\n </div>\r\n\r\n <div class=\"fb-top__actions\">\r\n <button type=\"button\" class=\"fb-btn\" [disabled]=\"!canUndo()\" aria-label=\"Annulla\" (click)=\"undo()\">\u21B6</button>\r\n <button type=\"button\" class=\"fb-btn\" [disabled]=\"!canRedo()\" aria-label=\"Ripeti\" (click)=\"redo()\">\u21B7</button>\r\n <button type=\"button\" class=\"fb-btn\" title=\"Ricalcola le posizioni\" (click)=\"autoLayout()\">Riordina</button>\r\n @if (isDialogMode()) {\r\n <!-- Il doppio click sul node fa la stessa cosa, ma non si vede: questo comando s\u00EC. -->\r\n <button\r\n type=\"button\"\r\n class=\"fb-btn\"\r\n [disabled]=\"!selectedName()\"\r\n title=\"Apri il dettaglio dell\u2019elemento selezionato\"\r\n (click)=\"openSelectedElement()\"\r\n >\r\n Dettaglio\r\n </button>\r\n }\r\n <button type=\"button\" class=\"fb-btn\" [disabled]=\"isBusy()\" (click)=\"validateNow()\">Valida</button>\r\n\r\n @switch (primaryCommand()) {\r\n @case ('newVersion') {\r\n <!-- Su una versione non modificabile il comando primario e' \"Nuova versione\" (\u00A78.1). -->\r\n <button type=\"button\" class=\"fb-btn fb-btn--primary\" [disabled]=\"isBusy()\" (click)=\"createNewVersion()\">\r\n Nuova versione\r\n </button>\r\n }\r\n @case ('create') {\r\n <button type=\"button\" class=\"fb-btn fb-btn--primary\" [disabled]=\"isBusy()\" (click)=\"save()\">\r\n Crea\r\n </button>\r\n }\r\n @default {\r\n <button type=\"button\" class=\"fb-btn fb-btn--primary\" [disabled]=\"isBusy()\" (click)=\"save()\">\r\n Salva\r\n </button>\r\n }\r\n }\r\n\r\n <button\r\n type=\"button\"\r\n class=\"fb-btn\"\r\n [disabled]=\"isBusy() || !canActivate()\"\r\n [title]=\"\r\n canActivate()\r\n ? 'Attiva questa versione'\r\n : 'L\u2019attivazione esige zero errori: correggili nel pannello dei problemi'\r\n \"\r\n (click)=\"activate()\"\r\n >\r\n Attiva\r\n </button>\r\n </div>\r\n </header>\r\n\r\n @if (conflict()) {\r\n <!-- \u00A79.3: qualcun altro ha salvato. Due strade, entrambe offerte. -->\r\n <div class=\"fb-banner fb-banner--warn\" role=\"alert\">\r\n <span>\r\n {{ conflict()?.message }}\r\n @if (conflict()?.conflictingAuthor) {\r\n Ha salvato {{ conflict()?.conflictingAuthor }}.\r\n }\r\n </span>\r\n <button type=\"button\" class=\"fb-btn fb-btn--icon\" (click)=\"reloadAfterConflict()\">Ricarica</button>\r\n <button type=\"button\" class=\"fb-btn fb-btn--icon\" (click)=\"saveAsNewVersionAfterConflict()\">\r\n Salva come nuova versione\r\n </button>\r\n <button type=\"button\" class=\"fb-btn fb-btn--ghost fb-btn--icon\" (click)=\"dismissConflict()\">\u00D7</button>\r\n </div>\r\n }\r\n\r\n @if (notice()) {\r\n <div\r\n class=\"fb-banner\"\r\n [class.fb-banner--error]=\"notice()?.kind === 'error'\"\r\n role=\"status\"\r\n >\r\n <span>{{ notice()?.message }}</span>\r\n <button type=\"button\" class=\"fb-btn fb-btn--ghost fb-btn--icon\" aria-label=\"Chiudi\" (click)=\"dismissNotice()\">\r\n \u00D7\r\n </button>\r\n </div>\r\n }\r\n\r\n @if (hasScreensInAutoLaunched()) {\r\n <div class=\"fb-banner fb-banner--error\">\r\n Un flow AutoLaunched che contiene screen e\u2019 un errore di validazione: non c\u2019e\u2019 nessuno a cui mostrarli.\r\n </div>\r\n }\r\n @if (isOrchestration()) {\r\n <div class=\"fb-banner fb-banner--warn\">Un flow Orchestration non e\u2019 eseguibile dal motore.</div>\r\n }\r\n @if (hasNoScreensInScreenFlow()) {\r\n <div class=\"fb-banner fb-banner--warn\">\r\n Un flow di tipo Screen senza nessuno screen viene segnalato dalla validazione.\r\n </div>\r\n }\r\n @if (!isEditable()) {\r\n <div class=\"fb-banner\">\r\n Questa versione e\u2019 in sola lettura: per modificarla creane una nuova.\r\n </div>\r\n }\r\n\r\n <div class=\"fb-main\">\r\n <aside class=\"fb-main__palette\">\r\n <fb-element-palette [processType]=\"document().processType\" (elementPicked)=\"onElementPicked($event)\" />\r\n </aside>\r\n\r\n <div class=\"fb-main__center\">\r\n <fb-flow-canvas\r\n class=\"fb-main__canvas\"\r\n [selectedName]=\"selectedName()\"\r\n [outline]=\"outline()\"\r\n (selectionChange)=\"onSelectionChange($event)\"\r\n (nodeOpened)=\"onNodeOpened($event)\"\r\n (nodeRemoveRequested)=\"onRemoveNode($event)\"\r\n (nodeDuplicateRequested)=\"onDuplicateNode($event)\"\r\n (elementDropped)=\"onElementDropped($event)\"\r\n />\r\n\r\n @if (showProblems()) {\r\n <fb-problems-panel\r\n class=\"fb-main__problems\"\r\n (elementFocused)=\"focusElement($event)\"\r\n (closed)=\"toggleProblems()\"\r\n />\r\n } @else {\r\n <button type=\"button\" class=\"fb-main__problems-toggle\" (click)=\"toggleProblems()\">\r\n Problemi\r\n @if (errorCount()) {\r\n <span class=\"fb-main__count fb-main__count--error\">{{ errorCount() }}</span>\r\n }\r\n @if (warningCount()) {\r\n <span class=\"fb-main__count fb-main__count--warn\">{{ warningCount() }}</span>\r\n }\r\n </button>\r\n }\r\n </div>\r\n\r\n <aside class=\"fb-main__side\">\r\n <nav class=\"fb-side__tabs\" aria-label=\"Pannelli\">\r\n @if (!isDialogMode()) {\r\n <button\r\n type=\"button\"\r\n class=\"fb-side__tab\"\r\n [class.fb-side__tab--active]=\"sidePanel() === 'inspector'\"\r\n (click)=\"setPanel('inspector')\"\r\n >\r\n Elemento\r\n </button>\r\n }\r\n <button\r\n type=\"button\"\r\n class=\"fb-side__tab\"\r\n [class.fb-side__tab--active]=\"sidePanel() === 'resources'\"\r\n (click)=\"setPanel('resources')\"\r\n >\r\n Risorse\r\n </button>\r\n <button\r\n type=\"button\"\r\n class=\"fb-side__tab\"\r\n [class.fb-side__tab--active]=\"sidePanel() === 'versions'\"\r\n (click)=\"setPanel('versions')\"\r\n >\r\n Versioni\r\n </button>\r\n <button\r\n type=\"button\"\r\n class=\"fb-side__tab\"\r\n [class.fb-side__tab--active]=\"sidePanel() === 'debug'\"\r\n (click)=\"setPanel('debug')\"\r\n >\r\n Prova\r\n </button>\r\n </nav>\r\n\r\n <div class=\"fb-side__content\">\r\n @switch (sidePanel()) {\r\n @case ('inspector') {\r\n <fb-element-inspector\r\n [selectedName]=\"selectedName()\"\r\n (removeRequested)=\"onRemoveNode($event)\"\r\n (duplicateRequested)=\"onDuplicateNode($event)\"\r\n (closed)=\"setPanel('resources')\"\r\n />\r\n }\r\n @case ('resources') {\r\n <fb-resource-panel (closed)=\"setPanel('inspector')\" />\r\n }\r\n @case ('versions') {\r\n <fb-version-panel\r\n (versionOpened)=\"openVersion($event)\"\r\n (notice)=\"showNotice($event)\"\r\n (closed)=\"setPanel('inspector')\"\r\n />\r\n }\r\n @case ('debug') {\r\n <!-- Evidenzia sul canvas senza rubare il pannello: l'interview e\u2019 in corso. -->\r\n <fb-debug-panel (elementFocused)=\"highlightElement($event)\" (closed)=\"setPanel('inspector')\" />\r\n }\r\n }\r\n </div>\r\n\r\n <footer class=\"fb-side__footer\">\r\n <label class=\"fb-btn fb-btn--icon\">\r\n Importa JSON\r\n <input type=\"file\" accept=\"application/json,.json\" hidden (change)=\"onFileSelected($event)\" />\r\n </label>\r\n </footer>\r\n </aside>\r\n </div>\r\n\r\n @if (isDialogMode() && isDialogOpen() && selectedName()) {\r\n <!-- La dialog sta dentro il builder, non nel body: la libreria e\u2019 innestabile. -->\r\n <fb-element-dialog\r\n [selectedName]=\"selectedName()\"\r\n (closed)=\"closeDialog()\"\r\n (removeRequested)=\"onRemoveNode($event)\"\r\n (duplicateRequested)=\"onDuplicateNode($event)\"\r\n />\r\n }\r\n</div>\r\n", styles: [":host{display:block;width:100%;height:100%;min-height:0;font:inherit;color:var(--fb-text, #1d2939)}.fb-builder{position:relative;display:flex;flex-direction:column;width:100%;height:100%;min-height:0;background:var(--fb-surface, #fff)}.fb-top{display:flex;align-items:center;justify-content:space-between;gap:12px;padding:9px 14px;border-bottom:1px solid var(--fb-border, #e2e5eb);background:var(--fb-surface, #fff)}.fb-top__identity{min-width:0}.fb-top__label{width:100%;max-width:420px;padding:2px 4px;border:1px solid transparent;border-radius:4px;background:transparent;color:var(--fb-text, #1d2939);font:inherit;font-size:15px;font-weight:600}.fb-top__label:hover:not(:disabled),.fb-top__label:focus-visible{border-color:var(--fb-border, #d6dae1);background:var(--fb-surface, #fff)}.fb-top__meta{display:flex;flex-wrap:wrap;align-items:center;gap:6px;margin-top:2px}.fb-top__name{width:180px;padding:1px 4px;border:1px solid transparent;border-radius:4px;background:transparent;color:var(--fb-text-muted, #667085);font-family:ui-monospace,SFMono-Regular,Menlo,monospace;font-size:11px}.fb-top__name:hover:not(:disabled),.fb-top__name:focus-visible{border-color:var(--fb-border, #d6dae1)}.fb-top__process{padding:1px 4px;border:1px solid var(--fb-border-subtle, #e6e9ee);border-radius:4px;background:transparent;color:var(--fb-text-muted, #667085);font:inherit;font-size:11px}.fb-top__status,.fb-top__version,.fb-top__dirty{padding:2px 7px;border-radius:999px;background:var(--fb-surface-sunken, #eef0f4);color:var(--fb-text-muted, #6b7086);font-size:10px;font-weight:600}.fb-top__version{font-family:ui-monospace,SFMono-Regular,Menlo,monospace}.fb-top__dirty{background:color-mix(in srgb,var(--fb-warning, #b7791f) 16%,transparent);color:var(--fb-warning, #b7791f)}.fb-top__actions{display:flex;flex-wrap:wrap;gap:4px}.fb-banner{display:flex;flex-wrap:wrap;align-items:center;gap:8px;padding:6px 12px;border-bottom:1px solid var(--fb-border-subtle, #e6e9ee);background:color-mix(in srgb,var(--fb-accent, #2f6feb) 7%,transparent);font-size:11px}.fb-banner--warn{background:color-mix(in srgb,var(--fb-warning, #b7791f) 12%,transparent);color:var(--fb-warning, #b7791f)}.fb-banner--error{background:color-mix(in srgb,var(--fb-error, #c9372c) 10%,transparent);color:var(--fb-error, #c9372c)}.fb-banner>span{flex:1;min-width:200px}.fb-main{display:flex;flex:1;min-height:0}.fb-main__palette{flex:0 0 190px;min-width:0}.fb-main__center{display:flex;flex:1;flex-direction:column;min-width:0;min-height:0}.fb-main__canvas{flex:1;min-height:0}.fb-main__problems{flex:0 0 auto;height:220px}.fb-main__problems-toggle{display:flex;align-items:center;gap:6px;padding:4px 12px;border:0;border-top:1px solid var(--fb-border, #d6dae1);background:var(--fb-surface-alt, #f8f9fb);color:var(--fb-text-muted, #667085);font:inherit;font-size:11px;cursor:pointer}.fb-main__count{padding:0 5px;border-radius:8px;background:var(--fb-border, #d6dae1);font-size:9px;font-weight:700}.fb-main__count--error{background:color-mix(in srgb,var(--fb-error, #c9372c) 16%,transparent);color:var(--fb-error, #c9372c)}.fb-main__count--warn{background:color-mix(in srgb,var(--fb-warning, #b7791f) 16%,transparent);color:var(--fb-warning, #b7791f)}.fb-main__side{display:flex;flex-direction:column;flex:0 0 340px;min-width:0;min-height:0;border-left:1px solid var(--fb-border, #d6dae1);background:var(--fb-surface, #fff)}.fb-side__tabs{display:flex;gap:2px;margin:8px 10px;padding:3px;border-radius:var(--fb-radius, 10px);background:var(--fb-surface-sunken, #eef0f4)}.fb-side__tab{flex:1;padding:5px 8px;border:0;border-radius:var(--fb-radius-xs, 6px);background:transparent;color:var(--fb-text-muted, #6b7086);font:inherit;font-size:11px;cursor:pointer;transition:background .12s ease,color .12s ease}.fb-side__tab:hover:not(.fb-side__tab--active){color:var(--fb-text, #1a1c23)}.fb-side__tab--active{background:var(--fb-surface, #fff);box-shadow:var(--fb-shadow-sm, 0 1px 2px rgb(16 24 40 / 6%));color:var(--fb-text, #1a1c23);font-weight:600}.fb-side__content{flex:1;min-height:0;border-top:1px solid var(--fb-border-subtle, #eef0f4)}.fb-side__content>*{height:100%}.fb-side__footer{display:flex;gap:6px;padding:6px 8px;border-top:1px solid var(--fb-border-subtle, #e6e9ee)}@media(max-width:1200px){.fb-main__palette{flex-basis:150px}.fb-main__side{flex-basis:290px}}\n"] }]
8091
+ ], changeDetection: ChangeDetectionStrategy.OnPush, template: "<div class=\"fb-builder\" [attr.data-fb-theme]=\"null\">\r\n <header class=\"fb-top\">\r\n <div class=\"fb-top__identity\">\r\n <input\r\n class=\"fb-top__label\"\r\n [value]=\"document().label || ''\"\r\n placeholder=\"Nome del flow\"\r\n aria-label=\"Nome del flow\"\r\n [disabled]=\"!isEditable()\"\r\n (input)=\"setLabel($any($event.target).value)\"\r\n />\r\n <div class=\"fb-top__meta\">\r\n <input\r\n class=\"fb-top__name\"\r\n [value]=\"document().fullName || ''\"\r\n placeholder=\"NomeTecnico\"\r\n aria-label=\"Nome tecnico del flow\"\r\n [disabled]=\"!isEditable()\"\r\n (input)=\"setFullName($any($event.target).value)\"\r\n />\r\n <select\r\n class=\"fb-top__process\"\r\n [fbValue]=\"document().processType || ''\"\r\n aria-label=\"Tipo di flow\"\r\n [disabled]=\"!isEditable()\"\r\n (change)=\"setProcessType($any($event.target).value)\"\r\n >\r\n @for (type of processTypes(); track type.value) {\r\n <option [value]=\"type.value\">{{ type.label }}</option>\r\n }\r\n </select>\r\n <span class=\"fb-top__status\">{{ statusLabel() }}</span>\r\n @if (session.version() !== null) {\r\n <span class=\"fb-top__version\">v{{ session.version() }}</span>\r\n }\r\n @if (isDirty()) {\r\n <span class=\"fb-top__dirty\" title=\"Ci sono modifiche non salvate\">modificato</span>\r\n }\r\n </div>\r\n </div>\r\n\r\n <div class=\"fb-top__actions\">\r\n <button type=\"button\" class=\"fb-btn\" [disabled]=\"!canUndo()\" aria-label=\"Annulla\" (click)=\"undo()\">\u21B6</button>\r\n <button type=\"button\" class=\"fb-btn\" [disabled]=\"!canRedo()\" aria-label=\"Ripeti\" (click)=\"redo()\">\u21B7</button>\r\n <button type=\"button\" class=\"fb-btn\" title=\"Ricalcola le posizioni\" (click)=\"autoLayout()\">Riordina</button>\r\n @if (isDialogMode()) {\r\n <!-- Il doppio click sul node fa la stessa cosa, ma non si vede: questo comando s\u00EC. -->\r\n <button\r\n type=\"button\"\r\n class=\"fb-btn\"\r\n [disabled]=\"!selectedName()\"\r\n title=\"Apri il dettaglio dell\u2019elemento selezionato\"\r\n (click)=\"openSelectedElement()\"\r\n >\r\n Dettaglio\r\n </button>\r\n }\r\n <button type=\"button\" class=\"fb-btn\" [disabled]=\"isBusy()\" (click)=\"validateNow()\">Valida</button>\r\n\r\n @switch (primaryCommand()) {\r\n @case ('newVersion') {\r\n <!-- Su una versione non modificabile il comando primario e' \"Nuova versione\" (\u00A78.1). -->\r\n <button type=\"button\" class=\"fb-btn fb-btn--primary\" [disabled]=\"isBusy()\" (click)=\"createNewVersion()\">\r\n Nuova versione\r\n </button>\r\n }\r\n @case ('create') {\r\n <button type=\"button\" class=\"fb-btn fb-btn--primary\" [disabled]=\"isBusy()\" (click)=\"save()\">\r\n Crea\r\n </button>\r\n }\r\n @default {\r\n <button type=\"button\" class=\"fb-btn fb-btn--primary\" [disabled]=\"isBusy()\" (click)=\"save()\">\r\n Salva\r\n </button>\r\n }\r\n }\r\n\r\n <button\r\n type=\"button\"\r\n class=\"fb-btn\"\r\n [disabled]=\"isBusy() || !canActivate()\"\r\n [title]=\"\r\n canActivate()\r\n ? 'Attiva questa versione'\r\n : 'L\u2019attivazione esige zero errori: correggili nel pannello dei problemi'\r\n \"\r\n (click)=\"activate()\"\r\n >\r\n Attiva\r\n </button>\r\n </div>\r\n </header>\r\n\r\n @if (conflict()) {\r\n <!-- \u00A79.3: qualcun altro ha salvato. Due strade, entrambe offerte. -->\r\n <div class=\"fb-banner fb-banner--warn\" role=\"alert\">\r\n <span>\r\n {{ conflict()?.message }}\r\n @if (conflict()?.conflictingAuthor) {\r\n Ha salvato {{ conflict()?.conflictingAuthor }}.\r\n }\r\n </span>\r\n <button type=\"button\" class=\"fb-btn fb-btn--icon\" (click)=\"reloadAfterConflict()\">Ricarica</button>\r\n <button type=\"button\" class=\"fb-btn fb-btn--icon\" (click)=\"saveAsNewVersionAfterConflict()\">\r\n Salva come nuova versione\r\n </button>\r\n <button type=\"button\" class=\"fb-btn fb-btn--ghost fb-btn--icon\" (click)=\"dismissConflict()\">\u00D7</button>\r\n </div>\r\n }\r\n\r\n @if (notice()) {\r\n <div\r\n class=\"fb-banner\"\r\n [class.fb-banner--error]=\"notice()?.kind === 'error'\"\r\n role=\"status\"\r\n >\r\n <span>{{ notice()?.message }}</span>\r\n <button type=\"button\" class=\"fb-btn fb-btn--ghost fb-btn--icon\" aria-label=\"Chiudi\" (click)=\"dismissNotice()\">\r\n \u00D7\r\n </button>\r\n </div>\r\n }\r\n\r\n @if (hasScreensInAutoLaunched()) {\r\n <div class=\"fb-banner fb-banner--error\">\r\n Un flow AutoLaunched che contiene screen e\u2019 un errore di validazione: non c\u2019e\u2019 nessuno a cui mostrarli.\r\n </div>\r\n }\r\n @if (isOrchestrationWithoutStages()) {\r\n <div class=\"fb-banner fb-banner--warn\">\r\n Un flow Orchestration senza stage di orchestrazione viene segnalato dalla validazione\r\n (ORCHESTRATION_WITHOUT_STAGES).\r\n </div>\r\n }\r\n @if (hasNoScreensInScreenFlow()) {\r\n <div class=\"fb-banner fb-banner--warn\">\r\n Un flow di tipo Screen senza nessuno screen viene segnalato dalla validazione.\r\n </div>\r\n }\r\n @if (!isEditable()) {\r\n <div class=\"fb-banner\">\r\n Questa versione e\u2019 in sola lettura: per modificarla creane una nuova.\r\n </div>\r\n }\r\n\r\n <div class=\"fb-main\">\r\n <aside class=\"fb-main__palette\">\r\n <fb-element-palette [processType]=\"document().processType\" (elementPicked)=\"onElementPicked($event)\" />\r\n </aside>\r\n\r\n <div class=\"fb-main__center\">\r\n <fb-flow-canvas\r\n class=\"fb-main__canvas\"\r\n [selectedName]=\"selectedName()\"\r\n [outline]=\"outline()\"\r\n (selectionChange)=\"onSelectionChange($event)\"\r\n (nodeOpened)=\"onNodeOpened($event)\"\r\n (nodeRemoveRequested)=\"onRemoveNode($event)\"\r\n (nodeDuplicateRequested)=\"onDuplicateNode($event)\"\r\n (elementDropped)=\"onElementDropped($event)\"\r\n />\r\n\r\n @if (showProblems()) {\r\n <fb-problems-panel\r\n class=\"fb-main__problems\"\r\n (elementFocused)=\"focusElement($event)\"\r\n (closed)=\"toggleProblems()\"\r\n />\r\n } @else {\r\n <button type=\"button\" class=\"fb-main__problems-toggle\" (click)=\"toggleProblems()\">\r\n Problemi\r\n @if (errorCount()) {\r\n <span class=\"fb-main__count fb-main__count--error\">{{ errorCount() }}</span>\r\n }\r\n @if (warningCount()) {\r\n <span class=\"fb-main__count fb-main__count--warn\">{{ warningCount() }}</span>\r\n }\r\n </button>\r\n }\r\n </div>\r\n\r\n <aside class=\"fb-main__side\">\r\n <nav class=\"fb-side__tabs\" aria-label=\"Pannelli\">\r\n @if (!isDialogMode()) {\r\n <button\r\n type=\"button\"\r\n class=\"fb-side__tab\"\r\n [class.fb-side__tab--active]=\"sidePanel() === 'inspector'\"\r\n (click)=\"setPanel('inspector')\"\r\n >\r\n Elemento\r\n </button>\r\n }\r\n <button\r\n type=\"button\"\r\n class=\"fb-side__tab\"\r\n [class.fb-side__tab--active]=\"sidePanel() === 'resources'\"\r\n (click)=\"setPanel('resources')\"\r\n >\r\n Risorse\r\n </button>\r\n <button\r\n type=\"button\"\r\n class=\"fb-side__tab\"\r\n [class.fb-side__tab--active]=\"sidePanel() === 'versions'\"\r\n (click)=\"setPanel('versions')\"\r\n >\r\n Versioni\r\n </button>\r\n <button\r\n type=\"button\"\r\n class=\"fb-side__tab\"\r\n [class.fb-side__tab--active]=\"sidePanel() === 'debug'\"\r\n (click)=\"setPanel('debug')\"\r\n >\r\n Prova\r\n </button>\r\n </nav>\r\n\r\n <div class=\"fb-side__content\">\r\n @switch (sidePanel()) {\r\n @case ('inspector') {\r\n <fb-element-inspector\r\n [selectedName]=\"selectedName()\"\r\n (removeRequested)=\"onRemoveNode($event)\"\r\n (duplicateRequested)=\"onDuplicateNode($event)\"\r\n (closed)=\"setPanel('resources')\"\r\n />\r\n }\r\n @case ('resources') {\r\n <fb-resource-panel (closed)=\"setPanel('inspector')\" />\r\n }\r\n @case ('versions') {\r\n <fb-version-panel\r\n (versionOpened)=\"openVersion($event)\"\r\n (notice)=\"showNotice($event)\"\r\n (closed)=\"setPanel('inspector')\"\r\n />\r\n }\r\n @case ('debug') {\r\n <!-- Evidenzia sul canvas senza rubare il pannello: l'interview e\u2019 in corso. -->\r\n <fb-debug-panel (elementFocused)=\"highlightElement($event)\" (closed)=\"setPanel('inspector')\" />\r\n }\r\n }\r\n </div>\r\n\r\n <footer class=\"fb-side__footer\">\r\n <label class=\"fb-btn fb-btn--icon\">\r\n Importa JSON\r\n <input type=\"file\" accept=\"application/json,.json\" hidden (change)=\"onFileSelected($event)\" />\r\n </label>\r\n </footer>\r\n </aside>\r\n </div>\r\n\r\n @if (isDialogMode() && isDialogOpen() && selectedName()) {\r\n <!-- La dialog sta dentro il builder, non nel body: la libreria e\u2019 innestabile. -->\r\n <fb-element-dialog\r\n [selectedName]=\"selectedName()\"\r\n (closed)=\"closeDialog()\"\r\n (removeRequested)=\"onRemoveNode($event)\"\r\n (duplicateRequested)=\"onDuplicateNode($event)\"\r\n />\r\n }\r\n</div>\r\n", styles: [":host{display:block;width:100%;height:100%;min-height:0;font:inherit;color:var(--fb-text, #1d2939)}.fb-builder{position:relative;display:flex;flex-direction:column;width:100%;height:100%;min-height:0;background:var(--fb-surface, #fff)}.fb-top{display:flex;align-items:center;justify-content:space-between;gap:12px;padding:9px 14px;border-bottom:1px solid var(--fb-border, #e2e5eb);background:var(--fb-surface, #fff)}.fb-top__identity{min-width:0}.fb-top__label{width:100%;max-width:420px;padding:2px 4px;border:1px solid transparent;border-radius:4px;background:transparent;color:var(--fb-text, #1d2939);font:inherit;font-size:15px;font-weight:600}.fb-top__label:hover:not(:disabled),.fb-top__label:focus-visible{border-color:var(--fb-border, #d6dae1);background:var(--fb-surface, #fff)}.fb-top__meta{display:flex;flex-wrap:wrap;align-items:center;gap:6px;margin-top:2px}.fb-top__name{width:180px;padding:1px 4px;border:1px solid transparent;border-radius:4px;background:transparent;color:var(--fb-text-muted, #667085);font-family:ui-monospace,SFMono-Regular,Menlo,monospace;font-size:11px}.fb-top__name:hover:not(:disabled),.fb-top__name:focus-visible{border-color:var(--fb-border, #d6dae1)}.fb-top__process{padding:1px 4px;border:1px solid var(--fb-border-subtle, #e6e9ee);border-radius:4px;background:transparent;color:var(--fb-text-muted, #667085);font:inherit;font-size:11px}.fb-top__status,.fb-top__version,.fb-top__dirty{padding:2px 7px;border-radius:999px;background:var(--fb-surface-sunken, #eef0f4);color:var(--fb-text-muted, #6b7086);font-size:10px;font-weight:600}.fb-top__version{font-family:ui-monospace,SFMono-Regular,Menlo,monospace}.fb-top__dirty{background:color-mix(in srgb,var(--fb-warning, #b7791f) 16%,transparent);color:var(--fb-warning, #b7791f)}.fb-top__actions{display:flex;flex-wrap:wrap;gap:4px}.fb-banner{display:flex;flex-wrap:wrap;align-items:center;gap:8px;padding:6px 12px;border-bottom:1px solid var(--fb-border-subtle, #e6e9ee);background:color-mix(in srgb,var(--fb-accent, #2f6feb) 7%,transparent);font-size:11px}.fb-banner--warn{background:color-mix(in srgb,var(--fb-warning, #b7791f) 12%,transparent);color:var(--fb-warning, #b7791f)}.fb-banner--error{background:color-mix(in srgb,var(--fb-error, #c9372c) 10%,transparent);color:var(--fb-error, #c9372c)}.fb-banner>span{flex:1;min-width:200px}.fb-main{display:flex;flex:1;min-height:0}.fb-main__palette{flex:0 0 190px;min-width:0}.fb-main__center{display:flex;flex:1;flex-direction:column;min-width:0;min-height:0}.fb-main__canvas{flex:1;min-height:0}.fb-main__problems{flex:0 0 auto;height:220px}.fb-main__problems-toggle{display:flex;align-items:center;gap:6px;padding:4px 12px;border:0;border-top:1px solid var(--fb-border, #d6dae1);background:var(--fb-surface-alt, #f8f9fb);color:var(--fb-text-muted, #667085);font:inherit;font-size:11px;cursor:pointer}.fb-main__count{padding:0 5px;border-radius:8px;background:var(--fb-border, #d6dae1);font-size:9px;font-weight:700}.fb-main__count--error{background:color-mix(in srgb,var(--fb-error, #c9372c) 16%,transparent);color:var(--fb-error, #c9372c)}.fb-main__count--warn{background:color-mix(in srgb,var(--fb-warning, #b7791f) 16%,transparent);color:var(--fb-warning, #b7791f)}.fb-main__side{display:flex;flex-direction:column;flex:0 0 340px;min-width:0;min-height:0;border-left:1px solid var(--fb-border, #d6dae1);background:var(--fb-surface, #fff)}.fb-side__tabs{display:flex;gap:2px;margin:8px 10px;padding:3px;border-radius:var(--fb-radius, 10px);background:var(--fb-surface-sunken, #eef0f4)}.fb-side__tab{flex:1;padding:5px 8px;border:0;border-radius:var(--fb-radius-xs, 6px);background:transparent;color:var(--fb-text-muted, #6b7086);font:inherit;font-size:11px;cursor:pointer;transition:background .12s ease,color .12s ease}.fb-side__tab:hover:not(.fb-side__tab--active){color:var(--fb-text, #1a1c23)}.fb-side__tab--active{background:var(--fb-surface, #fff);box-shadow:var(--fb-shadow-sm, 0 1px 2px rgb(16 24 40 / 6%));color:var(--fb-text, #1a1c23);font-weight:600}.fb-side__content{flex:1;min-height:0;border-top:1px solid var(--fb-border-subtle, #eef0f4)}.fb-side__content>*{height:100%}.fb-side__footer{display:flex;gap:6px;padding:6px 8px;border-top:1px solid var(--fb-border-subtle, #e6e9ee)}@media(max-width:1200px){.fb-main__palette{flex-basis:150px}.fb-main__side{flex-basis:290px}}\n"] }]
7263
8092
  }], ctorParameters: () => [], propDecorators: { flowName: [{ type: i0.Input, args: [{ isSignal: true, alias: "flowName", required: false }] }], version: [{ type: i0.Input, args: [{ isSignal: true, alias: "version", required: false }] }], author: [{ type: i0.Input, args: [{ isSignal: true, alias: "author", required: false }] }], defaultProcessType: [{ type: i0.Input, args: [{ isSignal: true, alias: "defaultProcessType", required: false }] }], inspectorMode: [{ type: i0.Input, args: [{ isSignal: true, alias: "inspectorMode", required: false }] }], saved: [{ type: i0.Output, args: ["saved"] }], activated: [{ type: i0.Output, args: ["activated"] }], closeRequested: [{ type: i0.Output, args: ["closeRequested"] }] } });
7264
8093
 
7265
8094
  /**
7266
8095
  * Generated bundle index. Do not edit.
7267
8096
  */
7268
8097
 
7269
- export { ConditionEditorComponent, ConnectorEditorComponent, DebugPanelComponent, ElementDialogComponent, ElementInspectorComponent, ElementPaletteComponent, FALLBACK_COLLECTION_BY_TYPE, FALLBACK_TYPE_LABEL, FLOW_BUILDER_HTTP_CONFIG, FLOW_ELEMENT_ICONS, FLOW_ERROR_FALLBACK_MESSAGE, FLOW_ERROR_HTTP_STATUS, FLOW_NAME_PATTERN, FLOW_NODE_COLLECTIONS, FLOW_NODE_HEIGHT, FLOW_NODE_WIDTH, FLOW_RESOURCE_COLLECTIONS, FieldAssignmentEditorComponent, FlowApiError, FlowBuilderApi, FlowBuilderComponent, FlowCanvasComponent, FlowCatalogStore, FlowDictionaryStore, FlowDocumentStore, FlowEditorSession, FlowLayoutService, FlowValidationStore, HttpFlowBuilderApi, NodeInspectorBase, ParameterEditorComponent, ProblemsPanelComponent, RecordFilterEditorComponent, ReferencePickerComponent, ResourcePanelComponent, START_NODE_NAME, SelectValueDirective, StartInspectorComponent, TYPES_WITH_AUTOMATIC_OUTPUT, TYPE_BY_COLLECTION, UNSUPPORTED_TYPES, ValueEditorComponent, VersionPanelComponent, canvasNodeId, checkConditionLogic, checkFlowName, elementIcon, emptyFlowDefinition, flowNodeWidth, flowNodeWidthClass, isCustomConditionLogic, isGlobalReference, isValidFlowName, moveCondition, outletByKey, outletsOf, parseCanvasNodeId, parseSourceConnectorId, parseTargetConnectorId, referenceRoot, remapConditionLogic, removeCondition, slugifyFlowName, sourceConnectorId, targetConnectorId, uniqueFlowName };
8098
+ export { ConditionEditorComponent, ConnectorEditorComponent, DebugPanelComponent, ElementDialogComponent, ElementInspectorComponent, ElementPaletteComponent, FALLBACK_COLLECTION_BY_TYPE, FALLBACK_TYPE_LABEL, FLOW_BUILDER_HTTP_CONFIG, FLOW_ELEMENT_ICONS, FLOW_ERROR_FALLBACK_MESSAGE, FLOW_ERROR_HTTP_STATUS, FLOW_NAME_PATTERN, FLOW_NODE_COLLECTIONS, FLOW_NODE_HEIGHT, FLOW_NODE_WIDTH, FLOW_RESOURCE_COLLECTIONS, FieldAssignmentEditorComponent, FlowApiError, FlowBuilderApi, FlowBuilderComponent, FlowCanvasComponent, FlowCatalogStore, FlowDictionaryStore, FlowDocumentStore, FlowEditorSession, FlowLayoutService, FlowValidationStore, HttpFlowBuilderApi, NodeInspectorBase, ORCHESTRATION_CONDITION_OUTPUT, OrchestratedStageInspectorComponent, ParameterEditorComponent, ProblemsPanelComponent, RecordFilterEditorComponent, ReferencePickerComponent, ResourcePanelComponent, START_NODE_NAME, SelectValueDirective, StartInspectorComponent, TYPES_WITH_AUTOMATIC_OUTPUT, TYPE_BY_COLLECTION, UNSUPPORTED_TYPES, ValueEditorComponent, VersionPanelComponent, areTypesComparable, canvasNodeId, checkConditionLogic, checkFlowName, elementIcon, emptyFlowDefinition, flowNodeWidth, flowNodeWidthClass, isCustomConditionLogic, isGlobalReference, isNumericType, isTypeCheckedOperator, isValidFlowName, moveCondition, outletByKey, outletsOf, parseCanvasNodeId, parseInvariantNumber, parseSourceConnectorId, parseTargetConnectorId, referenceRoot, remapConditionLogic, removeCondition, slugifyFlowName, sourceConnectorId, stageStepNames, stageStepOutputReferenced, stepsOf, targetConnectorId, uniqueFlowName };
7270
8099
  //# sourceMappingURL=esfaenza-flow-builder.mjs.map