@esfaenza/flow-builder 20.3.21 → 20.3.22
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +34 -14
- package/fesm2022/esfaenza-flow-builder.mjs +318 -298
- package/fesm2022/esfaenza-flow-builder.mjs.map +1 -1
- package/index.d.ts +179 -73
- package/package.json +1 -1
|
@@ -276,6 +276,45 @@ class FlowBuilderApi {
|
|
|
276
276
|
return missing('describeStructure');
|
|
277
277
|
}
|
|
278
278
|
// -------------------------------------------------------------------------
|
|
279
|
+
// Esecuzione: i comandi «Esegui» e «Debug» della barra
|
|
280
|
+
// -------------------------------------------------------------------------
|
|
281
|
+
//
|
|
282
|
+
// Questi due non sono primitive del contratto: sono il punto in cui l'editor **passa la
|
|
283
|
+
// mano**. L'editor chiede i valori di ingresso in una finestra e poi chiama; cosa succeda
|
|
284
|
+
// dopo — un runner dell'ospite, una scheda nuova, un debugger vero — non lo sa e non deve
|
|
285
|
+
// saperlo. Sono opzionali come tutte le primitive che dipendono da un servizio dell'host, e
|
|
286
|
+
// il rifiuto `MissingService` e' la risposta giusta: il comando si spegne con un messaggio,
|
|
287
|
+
// l'editor resta usabile.
|
|
288
|
+
/**
|
|
289
|
+
* «Esegui»: avvia il flow **salvato** con i valori raccolti dalla finestra.
|
|
290
|
+
*
|
|
291
|
+
* Chi integra l'editor la implementa. Non c'e' un default che «prova» il flow: l'editor non
|
|
292
|
+
* ha un motore, e nascondere l'assenza dietro un'esecuzione finta e' il modo piu' veloce di
|
|
293
|
+
* far credere che un flow funzioni.
|
|
294
|
+
*
|
|
295
|
+
* Il ritorno e' **facoltativo** ed e' l'unica cosa che l'editor sa dell'esecuzione: un
|
|
296
|
+
* {@link FlowRunOutcome} finisce nel pannello «Debug» — stato, traccia, risorse, output —
|
|
297
|
+
* mentre `void` significa «guardo altrove», e l'editor dice solo che e' partita. Serve
|
|
298
|
+
* soprattutto ai flow **senza schermate**: lì non c'e' nessuna interfaccia del runtime in cui
|
|
299
|
+
* vedere cos'e' successo, e la traccia accanto al grafo e' tutto cio' che si ha.
|
|
300
|
+
*/
|
|
301
|
+
runFlow(request) {
|
|
302
|
+
void request;
|
|
303
|
+
return missing('runFlow');
|
|
304
|
+
}
|
|
305
|
+
/**
|
|
306
|
+
* «Debug»: come {@link runFlow}, ma `request.debug` e' `true` — ed e' il caso in cui il
|
|
307
|
+
* ritorno conta davvero, perche' `trace` e `resources` esistono solo in debug (§6.5).
|
|
308
|
+
*
|
|
309
|
+
* Sono due metodi e non un flag perche' quasi sempre sono due strade diverse dell'ospite —
|
|
310
|
+
* l'una avvia e basta, l'altra apre un ispettore — e perche' un ambiente puo' esporre l'una
|
|
311
|
+
* senza l'altra: due `MissingService` distinti dicono quale delle due manca.
|
|
312
|
+
*/
|
|
313
|
+
debugFlow(request) {
|
|
314
|
+
void request;
|
|
315
|
+
return missing('debugFlow');
|
|
316
|
+
}
|
|
317
|
+
// -------------------------------------------------------------------------
|
|
279
318
|
// §6.5 Provare un flow dall'editor
|
|
280
319
|
// -------------------------------------------------------------------------
|
|
281
320
|
/**
|
|
@@ -12539,36 +12578,57 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.27", ngImpo
|
|
|
12539
12578
|
}], propDecorators: { closed: [{ type: i0.Output, args: ["closed"] }], versionOpened: [{ type: i0.Output, args: ["versionOpened"] }], notice: [{ type: i0.Output, args: ["notice"] }] } });
|
|
12540
12579
|
|
|
12541
12580
|
/**
|
|
12542
|
-
*
|
|
12543
|
-
*
|
|
12544
|
-
*
|
|
12545
|
-
*
|
|
12546
|
-
*
|
|
12547
|
-
*
|
|
12548
|
-
*
|
|
12549
|
-
*
|
|
12550
|
-
*
|
|
12551
|
-
*
|
|
12552
|
-
*
|
|
12553
|
-
*
|
|
12554
|
-
*
|
|
12555
|
-
*
|
|
12556
|
-
*
|
|
12557
|
-
* distinguono `isWaitingForEvent` / `isWaitingForStageStep`. Nel secondo caso si puo'
|
|
12558
|
-
* concludere lo step da qui, e la risposta puo' portare una `interviewKey` **nuova**: la
|
|
12559
|
-
* vecchia non e' piu' valida, quindi si sostituisce invece di conservarla (§5.14).
|
|
12581
|
+
* La finestra di «Esegui» e «Debug»: i valori di ingresso, prima di passare la mano all'ospite.
|
|
12582
|
+
*
|
|
12583
|
+
* L'editor non esegue niente. Qui si raccoglie l'unica cosa che l'esecuzione non puo' inventare
|
|
12584
|
+
* — i valori delle variabili `isInput`, cioe' il contratto del flow verso chi lo invoca (§4.6) —
|
|
12585
|
+
* e li si consegna a `FlowBuilderApi.runFlow` / `debugFlow`. Chi integra l'editor decide cosa
|
|
12586
|
+
* significhi «eseguire»; senza implementazione il comando fallisce con `MissingService` e
|
|
12587
|
+
* l'editor lo dice, invece di fingere un avvio.
|
|
12588
|
+
*
|
|
12589
|
+
* Tre cose che il form generato tratta come dette:
|
|
12590
|
+
* 1. il tipo dichiarato **vince** anche in scrittura (§6.5): un `Number` si manda come numero,
|
|
12591
|
+
* non come la stringa digitata, altrimenti il motore confronta una stringa con un numero.
|
|
12592
|
+
* 2. un `Structure` non ha un letterale (§4.7): si compila un membro alla volta e si manda
|
|
12593
|
+
* `className` piu' i soli membri valorizzati — la stessa forma che torna in lettura.
|
|
12594
|
+
* 3. un `Enum` si passa **per nome** (§4.6, §13.18), mai come numero; l'elenco vuoto significa
|
|
12595
|
+
* "non lo so" e allora il valore si scrive a mano.
|
|
12560
12596
|
*/
|
|
12561
|
-
class
|
|
12562
|
-
api = inject(FlowBuilderApi);
|
|
12597
|
+
class RunDialogComponent {
|
|
12563
12598
|
store = inject(FlowDocumentStore);
|
|
12564
|
-
session = inject(FlowEditorSession);
|
|
12565
12599
|
dictionaries = inject(FlowDictionaryStore);
|
|
12566
12600
|
catalog = inject(FlowCatalogStore);
|
|
12601
|
+
/** `debug` cambia il titolo, l'avviso sui dati e quale primitiva verra' chiamata. */
|
|
12602
|
+
mode = input.required(...(ngDevMode ? [{ debugName: "mode" }] : []));
|
|
12603
|
+
/** Il flow che verra' eseguito: e' quello **salvato**, non il documento in mano. */
|
|
12604
|
+
flowName = input(null, ...(ngDevMode ? [{ debugName: "flowName" }] : []));
|
|
12605
|
+
version = input(null, ...(ngDevMode ? [{ debugName: "version" }] : []));
|
|
12606
|
+
/** Con modifiche non salvate si esegue la versione salvata: dirlo evita la sorpresa. */
|
|
12607
|
+
isDirty = input(false, ...(ngDevMode ? [{ debugName: "isDirty" }] : []));
|
|
12608
|
+
/** L'ospite sta ancora rispondendo: il bottone resta spento invece di ripartire. */
|
|
12609
|
+
isBusy = input(false, ...(ngDevMode ? [{ debugName: "isBusy" }] : []));
|
|
12610
|
+
confirmed = output();
|
|
12611
|
+
cancelled = output();
|
|
12612
|
+
isDebug = computed(() => this.mode() === 'debug', ...(ngDevMode ? [{ debugName: "isDebug" }] : []));
|
|
12613
|
+
/** I valori compilati, come stringhe: la conversione al tipo avviene alla conferma. */
|
|
12614
|
+
rawValues = signal({}, ...(ngDevMode ? [{ debugName: "rawValues" }] : []));
|
|
12615
|
+
/** Le variabili `isInput` del flow: il contratto verso chi lo invoca (§4.6). */
|
|
12616
|
+
inputVariables = computed(() => this.store.document().variables?.filter((variable) => variable.isInput) ?? [], ...(ngDevMode ? [{ debugName: "inputVariables" }] : []));
|
|
12617
|
+
/**
|
|
12618
|
+
* §4.7 — i membri delle classi delle variabili `Structure` di input, per classe. Un'istanza si
|
|
12619
|
+
* compila un membro alla volta: non c'e' un letterale con cui scriverla in un campo solo.
|
|
12620
|
+
*/
|
|
12621
|
+
membersByClass = signal({}, ...(ngDevMode ? [{ debugName: "membersByClass" }] : []));
|
|
12622
|
+
/**
|
|
12623
|
+
* §4.6 — i valori dei tipi `Enum` che compaiono fra gli input. Un insieme chiuso merita una
|
|
12624
|
+
* tendina: qui si scrive un nome che il motore confrontera', e digitarlo e' il modo piu'
|
|
12625
|
+
* facile di sbagliarlo.
|
|
12626
|
+
*/
|
|
12627
|
+
valuesByEnumType = signal({}, ...(ngDevMode ? [{ debugName: "valuesByEnumType" }] : []));
|
|
12567
12628
|
constructor() {
|
|
12568
|
-
// Le classi delle variabili di input: servono a generare il form dei membri.
|
|
12569
12629
|
effect(() => {
|
|
12570
12630
|
const classes = new Set(this.inputVariables()
|
|
12571
|
-
.filter((variable) => this.
|
|
12631
|
+
.filter((variable) => this.isStructureVariable(variable))
|
|
12572
12632
|
.map((variable) => variable.objectType)
|
|
12573
12633
|
.filter((className) => !!className));
|
|
12574
12634
|
for (const className of classes) {
|
|
@@ -12578,15 +12638,10 @@ class DebugPanelComponent {
|
|
|
12578
12638
|
void this.catalog
|
|
12579
12639
|
.listStructureMembers(className)
|
|
12580
12640
|
.then((list) => this.membersByClass.update((current) => ({ ...current, [className]: list ?? [] })))
|
|
12581
|
-
// Catalogo non disponibile: nessun campo generato, e
|
|
12641
|
+
// Catalogo non disponibile: nessun campo generato, e la finestra lo dice.
|
|
12582
12642
|
.catch(() => this.membersByClass.update((current) => ({ ...current, [className]: [] })));
|
|
12583
12643
|
}
|
|
12584
12644
|
});
|
|
12585
|
-
/**
|
|
12586
|
-
* §4.6 — i valori dei tipi `Enum` che compaiono fra gli input, variabili e membri delle classi
|
|
12587
|
-
* insieme: qui si compila un valore che il motore confrontera' per nome, e un insieme chiuso
|
|
12588
|
-
* merita una tendina invece di una casella di testo. Un tipo senza valori resta digitabile.
|
|
12589
|
-
*/
|
|
12590
12645
|
effect(() => {
|
|
12591
12646
|
const types = new Set([
|
|
12592
12647
|
...this.inputVariables().map((variable) => variable.dataType === 'Enum' ? variable.objectType : undefined),
|
|
@@ -12607,130 +12662,25 @@ class DebugPanelComponent {
|
|
|
12607
12662
|
}
|
|
12608
12663
|
});
|
|
12609
12664
|
}
|
|
12610
|
-
|
|
12611
|
-
/** Chiede all'host di evidenziare l'elemento corrente dell'esecuzione. */
|
|
12612
|
-
elementFocused = output();
|
|
12613
|
-
result = signal(null, ...(ngDevMode ? [{ debugName: "result" }] : []));
|
|
12614
|
-
isRunning = signal(false, ...(ngDevMode ? [{ debugName: "isRunning" }] : []));
|
|
12615
|
-
errorMessage = signal(null, ...(ngDevMode ? [{ debugName: "errorMessage" }] : []));
|
|
12616
|
-
debugEnabled = signal(true, ...(ngDevMode ? [{ debugName: "debugEnabled" }] : []));
|
|
12617
|
-
/** I valori che l'utente compila nel piccolo form generato dalle variabili di input. */
|
|
12618
|
-
inputValues = signal({}, ...(ngDevMode ? [{ debugName: "inputValues" }] : []));
|
|
12619
|
-
/** I valori raccolti per lo screen corrente, per nome di `outputParameters`. */
|
|
12620
|
-
screenOutputs = signal({}, ...(ngDevMode ? [{ debugName: "screenOutputs" }] : []));
|
|
12621
|
-
/** Le variabili `isInput` del flow: il contratto verso chi lo invoca (§4.6). */
|
|
12622
|
-
inputVariables = computed(() => this.store.document().variables?.filter((variable) => variable.isInput) ?? [], ...(ngDevMode ? [{ debugName: "inputVariables" }] : []));
|
|
12623
|
-
/**
|
|
12624
|
-
* §4.7 — i membri delle classi delle variabili `Structure` di input, per classe. Un'istanza si
|
|
12625
|
-
* compila un membro alla volta: non c'e' un letterale con cui scriverla in un campo solo.
|
|
12626
|
-
*/
|
|
12627
|
-
membersByClass = signal({}, ...(ngDevMode ? [{ debugName: "membersByClass" }] : []));
|
|
12628
|
-
/** I membri **scrivibili** di una classe: gli altri li calcola il backend. */
|
|
12665
|
+
/** I membri **scrivibili** di una classe: gli altri li calcola il motore. */
|
|
12629
12666
|
membersOf(className) {
|
|
12630
12667
|
if (!className) {
|
|
12631
12668
|
return [];
|
|
12632
12669
|
}
|
|
12633
12670
|
return (this.membersByClass()[className] ?? []).filter((member) => member.isWritable !== false);
|
|
12634
12671
|
}
|
|
12635
|
-
/**
|
|
12636
|
-
* §4.6 — i valori di un tipo di enumerazione, per nome del tipo. Elenco vuoto significa "non lo
|
|
12637
|
-
* so" — tipo non indicato, primitiva assente, tipo senza valori dichiarati — e allora il valore
|
|
12638
|
-
* si scrive a mano.
|
|
12639
|
-
*/
|
|
12640
|
-
valuesByEnumType = signal({}, ...(ngDevMode ? [{ debugName: "valuesByEnumType" }] : []));
|
|
12641
12672
|
enumValuesOf(enumType) {
|
|
12642
12673
|
return enumType ? (this.valuesByEnumType()[enumType] ?? []) : [];
|
|
12643
12674
|
}
|
|
12644
12675
|
isStructureVariable(variable) {
|
|
12645
12676
|
return this.dictionaries.isStructure(variable.dataType);
|
|
12646
12677
|
}
|
|
12647
|
-
|
|
12648
|
-
|
|
12649
|
-
pendingScreen = computed(() => this.result()?.pendingScreen ?? null, ...(ngDevMode ? [{ debugName: "pendingScreen" }] : []));
|
|
12650
|
-
isWaitingForScreen = computed(() => this.status() === 'WaitingForScreen', ...(ngDevMode ? [{ debugName: "isWaitingForScreen" }] : []));
|
|
12651
|
-
/** §5.14 — l'attesa e' su uno step assegnato a una persona, non su un evento. */
|
|
12652
|
-
isWaitingForStageStep = computed(() => this.result()?.isWaitingForStageStep === true, ...(ngDevMode ? [{ debugName: "isWaitingForStageStep" }] : []));
|
|
12653
|
-
stageSteps = computed(() => this.result()?.stageSteps ?? [], ...(ngDevMode ? [{ debugName: "stageSteps" }] : []));
|
|
12654
|
-
/** Lo step su cui c'e' un work item aperto: e' quello che si puo' concludere. */
|
|
12655
|
-
waitingStageSteps = computed(() => this.stageSteps().filter((step) => step.isWaiting), ...(ngDevMode ? [{ debugName: "waitingStageSteps" }] : []));
|
|
12656
|
-
/** I valori con cui si conclude uno step, per nome di output. */
|
|
12657
|
-
stepOutputs = signal({}, ...(ngDevMode ? [{ debugName: "stepOutputs" }] : []));
|
|
12658
|
-
trace = computed(() => this.result()?.trace ?? [], ...(ngDevMode ? [{ debugName: "trace" }] : []));
|
|
12659
|
-
/** Le risorse come righe ordinate: il tipo dichiarato vince in lettura (§6.5). */
|
|
12660
|
-
resourceRows = computed(() => {
|
|
12661
|
-
const resources = this.result()?.resources ?? {};
|
|
12662
|
-
return Object.entries(resources)
|
|
12663
|
-
.map(([name, typed]) => ({
|
|
12664
|
-
name,
|
|
12665
|
-
type: typed?.type ?? '',
|
|
12666
|
-
value: DebugPanelComponent.render(typed),
|
|
12667
|
-
}))
|
|
12668
|
-
.sort((left, right) => left.name.localeCompare(right.name));
|
|
12669
|
-
}, ...(ngDevMode ? [{ debugName: "resourceRows" }] : []));
|
|
12670
|
-
outputRows = computed(() => {
|
|
12671
|
-
const outputs = this.result()?.outputs ?? {};
|
|
12672
|
-
return Object.entries(outputs).map(([name, typed]) => ({
|
|
12673
|
-
name,
|
|
12674
|
-
type: typed?.type ?? '',
|
|
12675
|
-
value: DebugPanelComponent.render(typed),
|
|
12676
|
-
}));
|
|
12677
|
-
}, ...(ngDevMode ? [{ debugName: "outputRows" }] : []));
|
|
12678
|
-
/** Gli input che lo screen corrente riceve, per mostrarli accanto al form generico. */
|
|
12679
|
-
screenInputRows = computed(() => {
|
|
12680
|
-
const inputs = this.pendingScreen()?.inputs ?? {};
|
|
12681
|
-
return Object.entries(inputs).map(([name, typed]) => ({
|
|
12682
|
-
name,
|
|
12683
|
-
type: typed?.type ?? '',
|
|
12684
|
-
value: DebugPanelComponent.render(typed),
|
|
12685
|
-
}));
|
|
12686
|
-
}, ...(ngDevMode ? [{ debugName: "screenInputRows" }] : []));
|
|
12687
|
-
/** I nomi degli output dichiarati dallo screen, per generare i campi da compilare. */
|
|
12688
|
-
screenOutputNames = computed(() => {
|
|
12689
|
-
const elementName = this.pendingScreen()?.elementName;
|
|
12690
|
-
if (!elementName) {
|
|
12691
|
-
return [];
|
|
12692
|
-
}
|
|
12693
|
-
const screen = this.store.document().screens?.find((candidate) => candidate.name === elementName);
|
|
12694
|
-
return (screen?.outputParameters ?? []).map((parameter) => parameter.name ?? '').filter(Boolean);
|
|
12695
|
-
}, ...(ngDevMode ? [{ debugName: "screenOutputNames" }] : []));
|
|
12696
|
-
/**
|
|
12697
|
-
* §6.5 — il tipo dichiarato vince, e due tipi hanno una forma propria invece di `value`: un
|
|
12698
|
-
* record e un'istanza di classe. Cercare `value` su un `Structure` mostrerebbe «—» pur avendo
|
|
12699
|
-
* i dati sotto gli occhi (§4.7).
|
|
12700
|
-
*/
|
|
12701
|
-
static render(typed) {
|
|
12702
|
-
if (!typed) {
|
|
12703
|
-
return '—';
|
|
12704
|
-
}
|
|
12705
|
-
if (typed.instance) {
|
|
12706
|
-
const members = Object.entries(typed.instance.members ?? {})
|
|
12707
|
-
.map(([name, member]) => `${name}: ${DebugPanelComponent.render(member)}`)
|
|
12708
|
-
.join(', ');
|
|
12709
|
-
return `${typed.instance.className ?? 'istanza'} { ${members} }`;
|
|
12710
|
-
}
|
|
12711
|
-
if (typed.record) {
|
|
12712
|
-
const fields = Object.entries(typed.record.fields ?? {})
|
|
12713
|
-
.map(([name, value]) => `${name}: ${value === null || value === undefined ? '—' : String(value)}`)
|
|
12714
|
-
.join(', ');
|
|
12715
|
-
const head = [typed.record.objectType, typed.record.id].filter(Boolean).join('#');
|
|
12716
|
-
return `${head || 'record'} { ${fields} }`;
|
|
12717
|
-
}
|
|
12718
|
-
if (typed.value === undefined || typed.value === null) {
|
|
12719
|
-
return '—';
|
|
12720
|
-
}
|
|
12721
|
-
if (typeof typed.value === 'object') {
|
|
12722
|
-
return JSON.stringify(typed.value);
|
|
12723
|
-
}
|
|
12724
|
-
return String(typed.value);
|
|
12725
|
-
}
|
|
12726
|
-
setDebugEnabled(value) {
|
|
12727
|
-
this.debugEnabled.set(value);
|
|
12678
|
+
setValue(key, value) {
|
|
12679
|
+
this.rawValues.update((values) => ({ ...values, [key]: value }));
|
|
12728
12680
|
}
|
|
12729
|
-
|
|
12730
|
-
|
|
12731
|
-
|
|
12732
|
-
setScreenOutput(name, value) {
|
|
12733
|
-
this.screenOutputs.update((values) => ({ ...values, [name]: value }));
|
|
12681
|
+
/** Il valore di un campo del form: serve a ripopolarlo se la finestra si ridisegna. */
|
|
12682
|
+
valueOf(key) {
|
|
12683
|
+
return this.rawValues()[key] ?? '';
|
|
12734
12684
|
}
|
|
12735
12685
|
/** Converte un valore del form nel tipo dichiarato: il tipo vince, anche in scrittura (§6.5). */
|
|
12736
12686
|
static coerce(raw, dataType) {
|
|
@@ -12749,21 +12699,19 @@ class DebugPanelComponent {
|
|
|
12749
12699
|
return raw;
|
|
12750
12700
|
}
|
|
12751
12701
|
}
|
|
12752
|
-
/**
|
|
12753
|
-
|
|
12754
|
-
|
|
12702
|
+
/**
|
|
12703
|
+
* I valori da mandare. Un campo lasciato vuoto **non** si manda: mandare la stringa vuota
|
|
12704
|
+
* significherebbe assegnare, e un input non valorizzato e' un'altra cosa da un input a "".
|
|
12705
|
+
*/
|
|
12706
|
+
collectInputs() {
|
|
12707
|
+
const values = this.rawValues();
|
|
12755
12708
|
const result = {};
|
|
12756
12709
|
for (const variable of this.inputVariables()) {
|
|
12757
12710
|
const name = variable.name;
|
|
12758
12711
|
if (!name) {
|
|
12759
12712
|
continue;
|
|
12760
12713
|
}
|
|
12761
|
-
|
|
12762
|
-
* §4.7 — un'istanza non ha un valore letterale: si scrive `className` piu' i soli membri
|
|
12763
|
-
* che si vogliono valorizzare, ed e' la **stessa** forma che il motore restituisce in
|
|
12764
|
-
* lettura. I membri stanno nel form sotto la chiave `Variabile.Membro`.
|
|
12765
|
-
*/
|
|
12766
|
-
if (this.dictionaries.isStructure(variable.dataType)) {
|
|
12714
|
+
if (this.isStructureVariable(variable)) {
|
|
12767
12715
|
const className = variable.objectType;
|
|
12768
12716
|
if (!className) {
|
|
12769
12717
|
continue;
|
|
@@ -12774,7 +12722,7 @@ class DebugPanelComponent {
|
|
|
12774
12722
|
if (raw === undefined || raw === '') {
|
|
12775
12723
|
continue;
|
|
12776
12724
|
}
|
|
12777
|
-
const value =
|
|
12725
|
+
const value = RunDialogComponent.coerce(raw, member.dataType);
|
|
12778
12726
|
if (value !== undefined) {
|
|
12779
12727
|
members[member.name] = { type: member.dataType, value };
|
|
12780
12728
|
}
|
|
@@ -12788,138 +12736,85 @@ class DebugPanelComponent {
|
|
|
12788
12736
|
if (raw === undefined || raw === '') {
|
|
12789
12737
|
continue;
|
|
12790
12738
|
}
|
|
12791
|
-
const value =
|
|
12739
|
+
const value = RunDialogComponent.coerce(raw, variable.dataType);
|
|
12792
12740
|
if (value !== undefined) {
|
|
12793
12741
|
result[name] = value;
|
|
12794
12742
|
}
|
|
12795
12743
|
}
|
|
12796
12744
|
return result;
|
|
12797
12745
|
}
|
|
12798
|
-
|
|
12799
|
-
|
|
12800
|
-
if (!flowName) {
|
|
12801
|
-
this.errorMessage.set('Salva il flow prima di provarlo.');
|
|
12802
|
-
return;
|
|
12803
|
-
}
|
|
12804
|
-
this.isRunning.set(true);
|
|
12805
|
-
this.errorMessage.set(null);
|
|
12806
|
-
this.screenOutputs.set({});
|
|
12807
|
-
try {
|
|
12808
|
-
const result = await this.api.startInterview({
|
|
12809
|
-
flowName,
|
|
12810
|
-
version: this.session.version() ?? undefined,
|
|
12811
|
-
inputs: this.coerceInputs(),
|
|
12812
|
-
debug: this.debugEnabled(),
|
|
12813
|
-
});
|
|
12814
|
-
this.apply(result);
|
|
12815
|
-
}
|
|
12816
|
-
catch (error) {
|
|
12817
|
-
this.handleError(error);
|
|
12818
|
-
}
|
|
12819
|
-
finally {
|
|
12820
|
-
this.isRunning.set(false);
|
|
12821
|
-
}
|
|
12822
|
-
}
|
|
12823
|
-
async respond(navigation) {
|
|
12824
|
-
const current = this.result();
|
|
12825
|
-
// Opaco: si rimanda tale e quale, senza leggerlo.
|
|
12826
|
-
const token = current?.continuationToken;
|
|
12827
|
-
if (!token) {
|
|
12828
|
-
return;
|
|
12829
|
-
}
|
|
12830
|
-
this.isRunning.set(true);
|
|
12831
|
-
this.errorMessage.set(null);
|
|
12832
|
-
try {
|
|
12833
|
-
const result = await this.api.respondToScreen({
|
|
12834
|
-
continuationToken: token,
|
|
12835
|
-
screenElementName: current?.pendingScreen?.elementName,
|
|
12836
|
-
navigation,
|
|
12837
|
-
// Con `Previous` e `Pause` il motore non memorizza gli output: si mandano comunque,
|
|
12838
|
-
// ma il pannello avverte che non sono definitivi.
|
|
12839
|
-
outputs: this.screenOutputs(),
|
|
12840
|
-
debug: this.debugEnabled(),
|
|
12841
|
-
});
|
|
12842
|
-
this.screenOutputs.set({});
|
|
12843
|
-
this.apply(result);
|
|
12844
|
-
}
|
|
12845
|
-
catch (error) {
|
|
12846
|
-
this.handleError(error);
|
|
12847
|
-
}
|
|
12848
|
-
finally {
|
|
12849
|
-
this.isRunning.set(false);
|
|
12850
|
-
}
|
|
12851
|
-
}
|
|
12852
|
-
setStepOutput(name, value) {
|
|
12853
|
-
this.stepOutputs.update((values) => ({ ...values, [name]: value }));
|
|
12746
|
+
confirm() {
|
|
12747
|
+
this.confirmed.emit(this.collectInputs());
|
|
12854
12748
|
}
|
|
12855
|
-
|
|
12856
|
-
|
|
12857
|
-
if (!stepName) {
|
|
12858
|
-
return [];
|
|
12859
|
-
}
|
|
12860
|
-
for (const stage of this.store.document().orchestratedStages ?? []) {
|
|
12861
|
-
const step = stage.stageSteps?.find((candidate) => candidate.name === stepName);
|
|
12862
|
-
if (step) {
|
|
12863
|
-
return (step.outputParameters ?? []).map((parameter) => parameter.name ?? '').filter(Boolean);
|
|
12864
|
-
}
|
|
12865
|
-
}
|
|
12866
|
-
return [];
|
|
12749
|
+
cancel() {
|
|
12750
|
+
this.cancelled.emit();
|
|
12867
12751
|
}
|
|
12752
|
+
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.3.27", ngImport: i0, type: RunDialogComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
|
|
12753
|
+
static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "20.3.27", type: RunDialogComponent, isStandalone: true, selector: "fb-run-dialog", inputs: { mode: { classPropertyName: "mode", publicName: "mode", isSignal: true, isRequired: true, transformFunction: null }, flowName: { classPropertyName: "flowName", publicName: "flowName", isSignal: true, isRequired: false, transformFunction: null }, version: { classPropertyName: "version", publicName: "version", isSignal: true, isRequired: false, transformFunction: null }, isDirty: { classPropertyName: "isDirty", publicName: "isDirty", isSignal: true, isRequired: false, transformFunction: null }, isBusy: { classPropertyName: "isBusy", publicName: "isBusy", isSignal: true, isRequired: false, transformFunction: null } }, outputs: { confirmed: "confirmed", cancelled: "cancelled" }, ngImport: i0, template: "<div class=\"fb-dialog__backdrop\" (click)=\"cancel()\"></div>\r\n\r\n<div\r\n class=\"fb-dialog__panel fb-run\"\r\n role=\"dialog\"\r\n aria-modal=\"true\"\r\n [attr.aria-label]=\"isDebug() ? 'Esegui in debug' : 'Esegui il flow'\"\r\n tabindex=\"-1\"\r\n (click)=\"$event.stopPropagation()\"\r\n (keydown.escape)=\"cancel()\"\r\n>\r\n <header class=\"fb-dialog__head\">\r\n <div class=\"fb-dialog__identity\">\r\n <span class=\"fb-dialog__type\">{{ isDebug() ? 'Debug' : 'Esecuzione' }}</span>\r\n <h2 class=\"fb-dialog__title\">\r\n {{ isDebug() ? 'Esegui in debug' : 'Esegui' }}\r\n @if (flowName()) {\r\n \u00AB{{ flowName() }}\u00BB\r\n }\r\n @if (version() != null) {\r\n <span class=\"fb-run__version\">v{{ version() }}</span>\r\n }\r\n </h2>\r\n </div>\r\n <button type=\"button\" class=\"fb-btn fb-btn--ghost fb-btn--icon\" aria-label=\"Chiudi\" (click)=\"cancel()\">\u00D7</button>\r\n </header>\r\n\r\n <div class=\"fb-dialog__body fb-run__body\">\r\n @if (isDirty()) {\r\n <!--\r\n Si esegue cio\u2019 che e\u2019 salvato: il documento in mano non e\u2019 ancora da nessuna parte.\r\n Dirlo qui evita la caccia al \u00ABperche\u2019 non vedo la modifica appena fatta\u00BB.\r\n -->\r\n <p class=\"fb-run__warn\">\r\n Ci sono modifiche non salvate: viene eseguita la <strong>versione salvata</strong>, non il documento\r\n aperto.\r\n </p>\r\n }\r\n\r\n <section class=\"fb-run__section\">\r\n <h3 class=\"fb-run__title\">Valori di ingresso</h3>\r\n @if (inputVariables().length) {\r\n <p class=\"fb-field__hint\">\r\n Le variabili di input del flow. Un campo lasciato vuoto non viene mandato: la variabile resta\r\n senza valore, che non e\u2019 la stessa cosa di una stringa vuota.\r\n </p>\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-run__type\">{{ variable.dataType }}{{ variable.isCollection ? '[]' : '' }}</span>\r\n </label>\r\n @if (isStructureVariable(variable)) {\r\n <!--\r\n \u00A74.7: un\u2019istanza non ha un letterale. Si compila un membro alla volta e si manda\r\n `className` piu\u2019 i soli membri valorizzati \u2014 la stessa forma che torna in lettura.\r\n -->\r\n <p class=\"fb-field__hint\">\r\n Istanza di <code>{{ variable.objectType }}</code>: valorizza i membri che ti servono.\r\n </p>\r\n @for (member of membersOf(variable.objectType); track member.name) {\r\n <div class=\"fb-field fb-run__member\">\r\n <label class=\"fb-field__label\">\r\n {{ member.name }}\r\n <span class=\"fb-run__type\">{{ member.dataType }}{{ member.isCollection ? '[]' : '' }}</span>\r\n </label>\r\n @if (member.dataType === 'Boolean') {\r\n <select\r\n class=\"fb-select\"\r\n [fbValue]=\"valueOf(variable.name + '.' + member.name)\"\r\n (change)=\"setValue(variable.name + '.' + member.name, $any($event.target).value)\"\r\n >\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 if (enumValuesOf(member.objectType).length) {\r\n <!-- \u00A74.6: i valori del tipo del membro, per nome. Il numero non si manda mai. -->\r\n <select\r\n class=\"fb-select\"\r\n [fbValue]=\"valueOf(variable.name + '.' + member.name)\"\r\n (change)=\"setValue(variable.name + '.' + member.name, $any($event.target).value)\"\r\n >\r\n <option value=\"\">\u2014</option>\r\n @for (entry of enumValuesOf(member.objectType); track entry.name) {\r\n <option [value]=\"entry.name\">{{ entry.label || entry.name }}</option>\r\n }\r\n </select>\r\n } @else {\r\n <input\r\n class=\"fb-input\"\r\n [type]=\"member.dataType === 'Number' || member.dataType === 'Integer' ? 'number' : 'text'\"\r\n [value]=\"valueOf(variable.name + '.' + member.name)\"\r\n (input)=\"setValue(variable.name + '.' + member.name, $any($event.target).value)\"\r\n />\r\n }\r\n </div>\r\n }\r\n @if (!membersOf(variable.objectType).length) {\r\n <p class=\"fb-field__hint\">\r\n Membri non disponibili: senza il catalogo della classe non c\u2019e\u2019 un form da generare.\r\n </p>\r\n }\r\n } @else if (variable.dataType === 'Boolean') {\r\n <select\r\n class=\"fb-select\"\r\n [fbValue]=\"valueOf(variable.name!)\"\r\n (change)=\"setValue(variable.name!, $any($event.target).value)\"\r\n >\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 if (enumValuesOf(variable.objectType).length) {\r\n <!--\r\n \u00A74.6: un enum si passa **per nome**, e i nomi ammessi sono quelli del tipo. Senza i\r\n valori \u2014 primitiva assente o tipo senza valori \u2014 si torna alla casella di testo.\r\n -->\r\n <select\r\n class=\"fb-select\"\r\n [fbValue]=\"valueOf(variable.name!)\"\r\n (change)=\"setValue(variable.name!, $any($event.target).value)\"\r\n >\r\n <option value=\"\">\u2014</option>\r\n @for (entry of enumValuesOf(variable.objectType); track entry.name) {\r\n <option [value]=\"entry.name\">{{ entry.label || entry.name }}</option>\r\n }\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 [value]=\"valueOf(variable.name!)\"\r\n (input)=\"setValue(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: non c\u2019e\u2019 niente da compilare.</p>\r\n }\r\n </section>\r\n\r\n @if (isDebug()) {\r\n <p class=\"fb-run__warn\">\r\n Il debug puo\u2019 riportare i valori di <strong>tutte</strong> le risorse, dati personali compresi: non\r\n usarlo su dati reali.\r\n </p>\r\n }\r\n </div>\r\n\r\n <footer class=\"fb-dialog__foot\">\r\n <span class=\"fb-dialog__note\">\r\n L\u2019esecuzione e\u2019 dell\u2019applicazione che ospita l\u2019editor: qui si raccolgono solo gli ingressi.\r\n </span>\r\n <div class=\"fb-run__actions\">\r\n <button type=\"button\" class=\"fb-btn\" (click)=\"cancel()\">Annulla</button>\r\n <button type=\"button\" class=\"fb-btn fb-btn--primary\" [disabled]=\"isBusy()\" (click)=\"confirm()\">\r\n {{ isDebug() ? 'Avvia in debug' : 'Avvia' }}\r\n </button>\r\n </div>\r\n </footer>\r\n</div>\r\n", styles: [":host{position:absolute;inset:0;z-index:40;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(520px,100%);max-height:min(720px,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,.fb-dialog__foot{display:flex;align-items:center;justify-content:space-between;gap:12px;padding:12px 14px}.fb-dialog__head{border-bottom:1px solid var(--fb-border-subtle, #eef0f4)}.fb-dialog__foot{border-top:1px solid var(--fb-border-subtle, #eef0f4)}.fb-dialog__type{display:block;font-size:11px;text-transform:uppercase;letter-spacing:.04em;color:var(--fb-text-muted, #6b7086)}.fb-dialog__title{margin:0;font-size:15px;font-weight:600}.fb-dialog__note{color:var(--fb-text-muted, #6b7086);font-size:12px}.fb-dialog__foot .fb-dialog__note{flex:1 1 auto;min-width:0}.fb-dialog__body{display:flex;flex-direction:column;gap:14px;padding:14px;overflow:auto}.fb-run__version{margin-left:4px;color:var(--fb-text-muted, #6b7086);font-weight:500;font-size:12px}.fb-run__section{display:flex;flex-direction:column;gap:8px}.fb-run__title{margin:0;font-size:12px;font-weight:600;text-transform:uppercase;letter-spacing:.04em;color:var(--fb-text-muted, #6b7086)}.fb-run__type{color:var(--fb-text-muted, #6b7086);font-size:11px;font-weight:400}.fb-run__member{padding-left:12px;border-left:2px solid var(--fb-border-subtle, #eef0f4)}.fb-run__warn{margin:0;padding:8px 10px;border-radius:var(--fb-radius-sm, 8px);background:var(--fb-surface-alt, #f7f8fa);color:var(--fb-warning, #b7791f);font-size:12px}.fb-run__actions{display:flex;flex:0 0 auto;gap:8px}.fb-run__actions .fb-btn{white-space:nowrap}\n"], dependencies: [{ kind: "directive", type: SelectValueDirective, selector: "select[fbValue]", inputs: ["fbValue"] }], changeDetection: i0.ChangeDetectionStrategy.OnPush });
|
|
12754
|
+
}
|
|
12755
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.27", ngImport: i0, type: RunDialogComponent, decorators: [{
|
|
12756
|
+
type: Component,
|
|
12757
|
+
args: [{ selector: 'fb-run-dialog', standalone: true, imports: [SelectValueDirective], changeDetection: ChangeDetectionStrategy.OnPush, template: "<div class=\"fb-dialog__backdrop\" (click)=\"cancel()\"></div>\r\n\r\n<div\r\n class=\"fb-dialog__panel fb-run\"\r\n role=\"dialog\"\r\n aria-modal=\"true\"\r\n [attr.aria-label]=\"isDebug() ? 'Esegui in debug' : 'Esegui il flow'\"\r\n tabindex=\"-1\"\r\n (click)=\"$event.stopPropagation()\"\r\n (keydown.escape)=\"cancel()\"\r\n>\r\n <header class=\"fb-dialog__head\">\r\n <div class=\"fb-dialog__identity\">\r\n <span class=\"fb-dialog__type\">{{ isDebug() ? 'Debug' : 'Esecuzione' }}</span>\r\n <h2 class=\"fb-dialog__title\">\r\n {{ isDebug() ? 'Esegui in debug' : 'Esegui' }}\r\n @if (flowName()) {\r\n \u00AB{{ flowName() }}\u00BB\r\n }\r\n @if (version() != null) {\r\n <span class=\"fb-run__version\">v{{ version() }}</span>\r\n }\r\n </h2>\r\n </div>\r\n <button type=\"button\" class=\"fb-btn fb-btn--ghost fb-btn--icon\" aria-label=\"Chiudi\" (click)=\"cancel()\">\u00D7</button>\r\n </header>\r\n\r\n <div class=\"fb-dialog__body fb-run__body\">\r\n @if (isDirty()) {\r\n <!--\r\n Si esegue cio\u2019 che e\u2019 salvato: il documento in mano non e\u2019 ancora da nessuna parte.\r\n Dirlo qui evita la caccia al \u00ABperche\u2019 non vedo la modifica appena fatta\u00BB.\r\n -->\r\n <p class=\"fb-run__warn\">\r\n Ci sono modifiche non salvate: viene eseguita la <strong>versione salvata</strong>, non il documento\r\n aperto.\r\n </p>\r\n }\r\n\r\n <section class=\"fb-run__section\">\r\n <h3 class=\"fb-run__title\">Valori di ingresso</h3>\r\n @if (inputVariables().length) {\r\n <p class=\"fb-field__hint\">\r\n Le variabili di input del flow. Un campo lasciato vuoto non viene mandato: la variabile resta\r\n senza valore, che non e\u2019 la stessa cosa di una stringa vuota.\r\n </p>\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-run__type\">{{ variable.dataType }}{{ variable.isCollection ? '[]' : '' }}</span>\r\n </label>\r\n @if (isStructureVariable(variable)) {\r\n <!--\r\n \u00A74.7: un\u2019istanza non ha un letterale. Si compila un membro alla volta e si manda\r\n `className` piu\u2019 i soli membri valorizzati \u2014 la stessa forma che torna in lettura.\r\n -->\r\n <p class=\"fb-field__hint\">\r\n Istanza di <code>{{ variable.objectType }}</code>: valorizza i membri che ti servono.\r\n </p>\r\n @for (member of membersOf(variable.objectType); track member.name) {\r\n <div class=\"fb-field fb-run__member\">\r\n <label class=\"fb-field__label\">\r\n {{ member.name }}\r\n <span class=\"fb-run__type\">{{ member.dataType }}{{ member.isCollection ? '[]' : '' }}</span>\r\n </label>\r\n @if (member.dataType === 'Boolean') {\r\n <select\r\n class=\"fb-select\"\r\n [fbValue]=\"valueOf(variable.name + '.' + member.name)\"\r\n (change)=\"setValue(variable.name + '.' + member.name, $any($event.target).value)\"\r\n >\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 if (enumValuesOf(member.objectType).length) {\r\n <!-- \u00A74.6: i valori del tipo del membro, per nome. Il numero non si manda mai. -->\r\n <select\r\n class=\"fb-select\"\r\n [fbValue]=\"valueOf(variable.name + '.' + member.name)\"\r\n (change)=\"setValue(variable.name + '.' + member.name, $any($event.target).value)\"\r\n >\r\n <option value=\"\">\u2014</option>\r\n @for (entry of enumValuesOf(member.objectType); track entry.name) {\r\n <option [value]=\"entry.name\">{{ entry.label || entry.name }}</option>\r\n }\r\n </select>\r\n } @else {\r\n <input\r\n class=\"fb-input\"\r\n [type]=\"member.dataType === 'Number' || member.dataType === 'Integer' ? 'number' : 'text'\"\r\n [value]=\"valueOf(variable.name + '.' + member.name)\"\r\n (input)=\"setValue(variable.name + '.' + member.name, $any($event.target).value)\"\r\n />\r\n }\r\n </div>\r\n }\r\n @if (!membersOf(variable.objectType).length) {\r\n <p class=\"fb-field__hint\">\r\n Membri non disponibili: senza il catalogo della classe non c\u2019e\u2019 un form da generare.\r\n </p>\r\n }\r\n } @else if (variable.dataType === 'Boolean') {\r\n <select\r\n class=\"fb-select\"\r\n [fbValue]=\"valueOf(variable.name!)\"\r\n (change)=\"setValue(variable.name!, $any($event.target).value)\"\r\n >\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 if (enumValuesOf(variable.objectType).length) {\r\n <!--\r\n \u00A74.6: un enum si passa **per nome**, e i nomi ammessi sono quelli del tipo. Senza i\r\n valori \u2014 primitiva assente o tipo senza valori \u2014 si torna alla casella di testo.\r\n -->\r\n <select\r\n class=\"fb-select\"\r\n [fbValue]=\"valueOf(variable.name!)\"\r\n (change)=\"setValue(variable.name!, $any($event.target).value)\"\r\n >\r\n <option value=\"\">\u2014</option>\r\n @for (entry of enumValuesOf(variable.objectType); track entry.name) {\r\n <option [value]=\"entry.name\">{{ entry.label || entry.name }}</option>\r\n }\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 [value]=\"valueOf(variable.name!)\"\r\n (input)=\"setValue(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: non c\u2019e\u2019 niente da compilare.</p>\r\n }\r\n </section>\r\n\r\n @if (isDebug()) {\r\n <p class=\"fb-run__warn\">\r\n Il debug puo\u2019 riportare i valori di <strong>tutte</strong> le risorse, dati personali compresi: non\r\n usarlo su dati reali.\r\n </p>\r\n }\r\n </div>\r\n\r\n <footer class=\"fb-dialog__foot\">\r\n <span class=\"fb-dialog__note\">\r\n L\u2019esecuzione e\u2019 dell\u2019applicazione che ospita l\u2019editor: qui si raccolgono solo gli ingressi.\r\n </span>\r\n <div class=\"fb-run__actions\">\r\n <button type=\"button\" class=\"fb-btn\" (click)=\"cancel()\">Annulla</button>\r\n <button type=\"button\" class=\"fb-btn fb-btn--primary\" [disabled]=\"isBusy()\" (click)=\"confirm()\">\r\n {{ isDebug() ? 'Avvia in debug' : 'Avvia' }}\r\n </button>\r\n </div>\r\n </footer>\r\n</div>\r\n", styles: [":host{position:absolute;inset:0;z-index:40;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(520px,100%);max-height:min(720px,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,.fb-dialog__foot{display:flex;align-items:center;justify-content:space-between;gap:12px;padding:12px 14px}.fb-dialog__head{border-bottom:1px solid var(--fb-border-subtle, #eef0f4)}.fb-dialog__foot{border-top:1px solid var(--fb-border-subtle, #eef0f4)}.fb-dialog__type{display:block;font-size:11px;text-transform:uppercase;letter-spacing:.04em;color:var(--fb-text-muted, #6b7086)}.fb-dialog__title{margin:0;font-size:15px;font-weight:600}.fb-dialog__note{color:var(--fb-text-muted, #6b7086);font-size:12px}.fb-dialog__foot .fb-dialog__note{flex:1 1 auto;min-width:0}.fb-dialog__body{display:flex;flex-direction:column;gap:14px;padding:14px;overflow:auto}.fb-run__version{margin-left:4px;color:var(--fb-text-muted, #6b7086);font-weight:500;font-size:12px}.fb-run__section{display:flex;flex-direction:column;gap:8px}.fb-run__title{margin:0;font-size:12px;font-weight:600;text-transform:uppercase;letter-spacing:.04em;color:var(--fb-text-muted, #6b7086)}.fb-run__type{color:var(--fb-text-muted, #6b7086);font-size:11px;font-weight:400}.fb-run__member{padding-left:12px;border-left:2px solid var(--fb-border-subtle, #eef0f4)}.fb-run__warn{margin:0;padding:8px 10px;border-radius:var(--fb-radius-sm, 8px);background:var(--fb-surface-alt, #f7f8fa);color:var(--fb-warning, #b7791f);font-size:12px}.fb-run__actions{display:flex;flex:0 0 auto;gap:8px}.fb-run__actions .fb-btn{white-space:nowrap}\n"] }]
|
|
12758
|
+
}], ctorParameters: () => [], propDecorators: { mode: [{ type: i0.Input, args: [{ isSignal: true, alias: "mode", required: true }] }], flowName: [{ type: i0.Input, args: [{ isSignal: true, alias: "flowName", required: false }] }], version: [{ type: i0.Input, args: [{ isSignal: true, alias: "version", required: false }] }], isDirty: [{ type: i0.Input, args: [{ isSignal: true, alias: "isDirty", required: false }] }], isBusy: [{ type: i0.Input, args: [{ isSignal: true, alias: "isBusy", required: false }] }], confirmed: [{ type: i0.Output, args: ["confirmed"] }], cancelled: [{ type: i0.Output, args: ["cancelled"] }] } });
|
|
12759
|
+
|
|
12760
|
+
/**
|
|
12761
|
+
* Il pannello «Debug»: cosa e' successo nell'ultima esecuzione lanciata dalla barra.
|
|
12762
|
+
*
|
|
12763
|
+
* Non conduce niente — l'esecuzione e' dell'applicazione ospite, e questo pannello **guarda**
|
|
12764
|
+
* cio' che `runFlow`/`debugFlow` hanno restituito. Esiste per il caso in cui non c'e' nient'altro
|
|
12765
|
+
* da guardare: un flow senza schermate (`AutoLaunched`, o un'orchestrazione tutta in background)
|
|
12766
|
+
* non apre nessuna interfaccia del runtime, quindi la traccia accanto al grafo e' l'unico modo di
|
|
12767
|
+
* sapere quali elementi sono stati eseguiti e in che ordine. Da lì si torna sull'elemento con un
|
|
12768
|
+
* click, che e' il motivo per cui sta nell'editor e non altrove.
|
|
12769
|
+
*
|
|
12770
|
+
* Tre cose che tratta come dette (§6.5):
|
|
12771
|
+
* 1. `Failed` **non** e' un errore di trasporto: la chiamata e' riuscita, il flow e' fallito.
|
|
12772
|
+
* E `NotStarted` significa che i criteri dello Start non erano soddisfatti.
|
|
12773
|
+
* 2. `trace` e `resources` arrivano solo con `debug: true`, e riportano i valori di **tutte**
|
|
12774
|
+
* le risorse — dati personali compresi: l'avviso e' in chiaro, non in una tooltip.
|
|
12775
|
+
* 3. l'ospite puo' non restituire niente: allora non c'e' niente da mostrare, e il pannello lo
|
|
12776
|
+
* dice invece di sembrare rotto.
|
|
12777
|
+
*/
|
|
12778
|
+
class DebugPanelComponent {
|
|
12779
|
+
/** L'esito dell'ultima esecuzione; `null` = non ne e' ancora stata lanciata una. */
|
|
12780
|
+
outcome = input(null, ...(ngDevMode ? [{ debugName: "outcome" }] : []));
|
|
12781
|
+
/** L'ultima esecuzione era in debug: senza, traccia e risorse non arrivano ed e' normale. */
|
|
12782
|
+
wasDebug = input(false, ...(ngDevMode ? [{ debugName: "wasDebug" }] : []));
|
|
12783
|
+
/** L'ospite sta ancora eseguendo: l'esito, se arriva, arriva dopo. */
|
|
12784
|
+
isRunning = input(false, ...(ngDevMode ? [{ debugName: "isRunning" }] : []));
|
|
12785
|
+
closed = output();
|
|
12786
|
+
/** Chiede all'host di portare l'utente sull'elemento: e' l'ancora della traccia. */
|
|
12787
|
+
elementFocused = output();
|
|
12788
|
+
cleared = output();
|
|
12789
|
+
status = computed(() => this.outcome()?.status ?? null, ...(ngDevMode ? [{ debugName: "status" }] : []));
|
|
12790
|
+
trace = computed(() => this.outcome()?.trace ?? [], ...(ngDevMode ? [{ debugName: "trace" }] : []));
|
|
12791
|
+
/** Le risorse come righe ordinate: il tipo dichiarato vince in lettura (§6.5). */
|
|
12792
|
+
resourceRows = computed(() => rowsOf(this.outcome()?.resources).sort((left, right) => left.name.localeCompare(right.name)), ...(ngDevMode ? [{ debugName: "resourceRows" }] : []));
|
|
12793
|
+
outputRows = computed(() => rowsOf(this.outcome()?.outputs), ...(ngDevMode ? [{ debugName: "outputRows" }] : []));
|
|
12868
12794
|
/**
|
|
12869
|
-
*
|
|
12870
|
-
*
|
|
12795
|
+
* L'ospite ha restituito qualcosa, ma niente di leggibile: nessuno stato, nessuna traccia,
|
|
12796
|
+
* nessun valore. Va distinto dal «non e' stata lanciata nessuna esecuzione», perche' il
|
|
12797
|
+
* rimedio e' diverso — lì si preme «Esegui», qui manca un pezzo dell'implementazione.
|
|
12871
12798
|
*/
|
|
12872
|
-
|
|
12873
|
-
const
|
|
12874
|
-
if (!
|
|
12875
|
-
return;
|
|
12876
|
-
}
|
|
12877
|
-
this.isRunning.set(true);
|
|
12878
|
-
this.errorMessage.set(null);
|
|
12879
|
-
try {
|
|
12880
|
-
const result = await this.api.completeStageStep({
|
|
12881
|
-
interviewKey: key,
|
|
12882
|
-
stepName,
|
|
12883
|
-
status,
|
|
12884
|
-
stepOutputs: this.stepOutputs(),
|
|
12885
|
-
debug: this.debugEnabled(),
|
|
12886
|
-
});
|
|
12887
|
-
this.stepOutputs.set({});
|
|
12888
|
-
// La risposta puo' portare una chiave nuova: `apply` sostituisce il risultato intero,
|
|
12889
|
-
// quindi la vecchia chiave non resta in giro.
|
|
12890
|
-
this.apply(result);
|
|
12891
|
-
}
|
|
12892
|
-
catch (error) {
|
|
12893
|
-
this.handleError(error);
|
|
12894
|
-
}
|
|
12895
|
-
finally {
|
|
12896
|
-
this.isRunning.set(false);
|
|
12897
|
-
}
|
|
12898
|
-
}
|
|
12899
|
-
apply(result) {
|
|
12900
|
-
this.result.set(result);
|
|
12901
|
-
if (result.currentElementName) {
|
|
12902
|
-
this.elementFocused.emit(result.currentElementName);
|
|
12903
|
-
}
|
|
12904
|
-
}
|
|
12905
|
-
handleError(error) {
|
|
12906
|
-
if (FlowApiError.is(error)) {
|
|
12907
|
-
// `InvalidToken` significa che la sessione di prova non e' piu' valida: si riavvia.
|
|
12908
|
-
this.errorMessage.set(error.message);
|
|
12909
|
-
return;
|
|
12799
|
+
isEmptyOutcome = computed(() => {
|
|
12800
|
+
const outcome = this.outcome();
|
|
12801
|
+
if (!outcome) {
|
|
12802
|
+
return false;
|
|
12910
12803
|
}
|
|
12911
|
-
|
|
12912
|
-
|
|
12913
|
-
|
|
12914
|
-
|
|
12915
|
-
|
|
12916
|
-
|
|
12917
|
-
|
|
12918
|
-
}
|
|
12804
|
+
return (!outcome.status &&
|
|
12805
|
+
!outcome.trace?.length &&
|
|
12806
|
+
!outcome.errors?.length &&
|
|
12807
|
+
!outcome.fault &&
|
|
12808
|
+
!this.resourceRows().length &&
|
|
12809
|
+
!this.outputRows().length);
|
|
12810
|
+
}, ...(ngDevMode ? [{ debugName: "isEmptyOutcome" }] : []));
|
|
12919
12811
|
close() {
|
|
12920
12812
|
this.closed.emit();
|
|
12921
12813
|
}
|
|
12922
|
-
|
|
12814
|
+
clear() {
|
|
12815
|
+
this.cleared.emit();
|
|
12816
|
+
}
|
|
12817
|
+
/** L'etichetta dello stato, che non e' un errore anche quando dice «Fallita» (§6.5). */
|
|
12923
12818
|
statusLabel() {
|
|
12924
12819
|
switch (this.status()) {
|
|
12925
12820
|
case 'Completed':
|
|
@@ -12935,45 +12830,68 @@ class DebugPanelComponent {
|
|
|
12935
12830
|
case 'NotStarted':
|
|
12936
12831
|
return 'Non avviata';
|
|
12937
12832
|
default:
|
|
12938
|
-
return '';
|
|
12833
|
+
return this.status() ?? '';
|
|
12939
12834
|
}
|
|
12940
12835
|
}
|
|
12941
12836
|
statusNote() {
|
|
12942
12837
|
switch (this.status()) {
|
|
12943
12838
|
case 'Failed':
|
|
12944
|
-
return 'Il flow e’ fallito: la
|
|
12839
|
+
return 'Il flow e’ fallito: la chiamata e’ andata a buon fine, l’esecuzione no.';
|
|
12945
12840
|
case 'NotStarted':
|
|
12946
12841
|
return 'I criteri dello Start non erano soddisfatti: non e’ un errore.';
|
|
12842
|
+
case 'WaitingForScreen':
|
|
12843
|
+
return 'L’esecuzione e’ ferma su una schermata: a mostrarla e’ l’applicazione, non l’editor.';
|
|
12947
12844
|
case 'Paused':
|
|
12948
|
-
return 'L’esecuzione e’ in pausa: puo’ essere ripresa dalla sua chiave.';
|
|
12949
12845
|
case 'Suspended':
|
|
12950
|
-
|
|
12951
|
-
if (this.isWaitingForStageStep()) {
|
|
12952
|
-
return 'Sospesa su uno step di orchestrazione: aspetta che un assegnatario lo concluda.';
|
|
12953
|
-
}
|
|
12954
|
-
return this.result()?.isWaitingForEvent
|
|
12955
|
-
? 'Sospesa in attesa di un evento: puo’ essere ripresa dalla sua chiave.'
|
|
12956
|
-
: 'L’esecuzione e’ sospesa: puo’ essere ripresa dalla sua chiave.';
|
|
12846
|
+
return 'L’esecuzione non e’ finita: puo’ essere ripresa da chi la conduce.';
|
|
12957
12847
|
default:
|
|
12958
12848
|
return null;
|
|
12959
12849
|
}
|
|
12960
12850
|
}
|
|
12961
|
-
canGoBack() {
|
|
12962
|
-
return this.pendingScreen()?.canGoBack === true;
|
|
12963
|
-
}
|
|
12964
|
-
canFinish() {
|
|
12965
|
-
return this.pendingScreen()?.canFinish === true;
|
|
12966
|
-
}
|
|
12967
|
-
canPause() {
|
|
12968
|
-
return this.pendingScreen()?.canPause === true;
|
|
12969
|
-
}
|
|
12970
12851
|
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.3.27", ngImport: i0, type: DebugPanelComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
|
|
12971
|
-
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 (isStructureVariable(variable)) {\r\n <!--\r\n \u00A74.7: un'istanza non ha un letterale. Si compila un membro alla volta e si manda\r\n `className` piu' i soli membri valorizzati \u2014 la stessa forma che torna in lettura.\r\n -->\r\n <p class=\"fb-field__hint\">\r\n Istanza di <code>{{ variable.objectType }}</code>: valorizza i membri che ti servono.\r\n </p>\r\n @for (member of membersOf(variable.objectType); track member.name) {\r\n <div class=\"fb-field\">\r\n <label class=\"fb-field__label\">\r\n {{ member.name }}\r\n <span class=\"fb-dbg__type\">{{ member.dataType }}{{ member.isCollection ? '[]' : '' }}</span>\r\n </label>\r\n @if (member.dataType === 'Boolean') {\r\n <select\r\n class=\"fb-select\"\r\n (change)=\"setInputValue(variable.name + '.' + member.name, $any($event.target).value)\"\r\n >\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 if (enumValuesOf(member.objectType).length) {\r\n <!-- \u00A74.6: i valori del tipo del membro, per nome. Il numero e' solo mostrato. -->\r\n <select\r\n class=\"fb-select\"\r\n (change)=\"setInputValue(variable.name + '.' + member.name, $any($event.target).value)\"\r\n >\r\n <option value=\"\">\u2014</option>\r\n @for (entry of enumValuesOf(member.objectType); track entry.name) {\r\n <option [value]=\"entry.name\">{{ entry.label || entry.name }}</option>\r\n }\r\n </select>\r\n } @else {\r\n <input\r\n class=\"fb-input\"\r\n [type]=\"member.dataType === 'Number' || member.dataType === 'Integer' ? 'number' : 'text'\"\r\n (input)=\"setInputValue(variable.name + '.' + member.name, $any($event.target).value)\"\r\n />\r\n }\r\n </div>\r\n }\r\n @if (!membersOf(variable.objectType).length) {\r\n <p class=\"fb-field__hint\">\r\n Membri non disponibili: senza il catalogo della classe non c\u2019e\u2019 un form da generare.\r\n </p>\r\n }\r\n } @else 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 if (enumValuesOf(variable.objectType).length) {\r\n <!--\r\n \u00A74.6: un enum si passa **per nome**, e i nomi ammessi sono quelli del tipo. Senza i\r\n valori \u2014 primitiva assente o tipo senza valori \u2014 si torna alla casella di testo.\r\n -->\r\n <select class=\"fb-select\" (change)=\"setInputValue(variable.name!, $any($event.target).value)\">\r\n <option value=\"\">\u2014</option>\r\n @for (entry of enumValuesOf(variable.objectType); track entry.name) {\r\n <option [value]=\"entry.name\">{{ entry.label || entry.name }}</option>\r\n }\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 });
|
|
12852
|
+
static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "20.3.27", type: DebugPanelComponent, isStandalone: true, selector: "fb-debug-panel", inputs: { outcome: { classPropertyName: "outcome", publicName: "outcome", isSignal: true, isRequired: false, transformFunction: null }, wasDebug: { classPropertyName: "wasDebug", publicName: "wasDebug", isSignal: true, isRequired: false, transformFunction: null }, isRunning: { classPropertyName: "isRunning", publicName: "isRunning", isSignal: true, isRequired: false, transformFunction: null } }, outputs: { closed: "closed", elementFocused: "elementFocused", cleared: "cleared" }, ngImport: i0, template: "<header class=\"fb-dbg__header\">\r\n <h2 class=\"fb-dbg__title\">Debug</h2>\r\n @if (outcome()) {\r\n <button type=\"button\" class=\"fb-btn fb-btn--ghost fb-btn--icon\" title=\"Svuota\" (click)=\"clear()\">Svuota</button>\r\n }\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 (isRunning()) {\r\n <p class=\"fb-field__hint\">Esecuzione in corso\u2026</p>\r\n }\r\n\r\n @if (!outcome() && !isRunning()) {\r\n <!--\r\n Non e\u2019 un errore: e\u2019 lo stato iniziale. Il pannello serve soprattutto ai flow senza\r\n schermate, dove non c\u2019e\u2019 nessun\u2019altra interfaccia in cui guardare la traccia.\r\n -->\r\n <p class=\"fb-field__hint\">\r\n Nessuna esecuzione. Usa <strong>Esegui</strong> o <strong>Debug</strong> nella barra in alto: qui\r\n compare cio\u2019 che l\u2019applicazione restituisce \u2014 stato, traccia degli elementi eseguiti, risorse e\r\n output.\r\n </p>\r\n }\r\n\r\n @if (isEmptyOutcome()) {\r\n <!--\r\n L\u2019ospite ha risposto, ma senza niente da mostrare. Distinto dal caso \u00ABnessuna esecuzione\u00BB\r\n perche\u2019 il rimedio e\u2019 un altro: qui manca un pezzo dell\u2019implementazione di runFlow.\r\n -->\r\n <p class=\"fb-callout\">\r\n L\u2019esecuzione e\u2019 partita, ma l\u2019applicazione non ha restituito nessun dettaglio da mostrare.\r\n </p>\r\n }\r\n\r\n @if (outcome(); as run) {\r\n @if (status() || run.steps) {\r\n <div class=\"fb-dbg__status\">\r\n @if (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]=\"status() === 'WaitingForScreen'\"\r\n >\r\n {{ statusLabel() }}\r\n </span>\r\n }\r\n @if (run.currentElementName) {\r\n <!-- Dove si e\u2019 fermata: cliccarlo porta sull\u2019elemento, come le righe della traccia. -->\r\n <button type=\"button\" class=\"fb-dbg__current\" (click)=\"elementFocused.emit(run.currentElementName!)\">\r\n su {{ run.currentElementName }}\r\n </button>\r\n }\r\n @if (run.steps) {\r\n <span class=\"fb-dbg__steps\">{{ run.steps }} passi</span>\r\n }\r\n </div>\r\n }\r\n\r\n @if (statusNote()) {\r\n <p class=\"fb-callout\">{{ statusNote() }}</p>\r\n }\r\n\r\n @if (run.fault) {\r\n <p class=\"fb-callout fb-callout--error\">{{ run.fault }}</p>\r\n }\r\n @for (message of run.errors || []; track $index) {\r\n <p class=\"fb-callout fb-callout--error\">{{ message }}</p>\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 <p class=\"fb-callout fb-callout--warn\">\r\n La traccia riporta i valori di <strong>tutte</strong> le risorse, dati personali compresi.\r\n </p>\r\n <ol class=\"fb-dbg__trace\">\r\n @for (entry of trace(); track $index) {\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 <!-- L\u2019ancora: dalla riga della traccia all\u2019elemento sul canvas. -->\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 } @else if (!wasDebug() && !isEmptyOutcome()) {\r\n <!-- Non e\u2019 un difetto: `trace` e `resources` esistono solo con `debug: true` (\u00A76.5). -->\r\n <p class=\"fb-field__hint\">\r\n Traccia e risorse arrivano solo con il comando <strong>Debug</strong>.\r\n </p>\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</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;gap:6px;padding:10px 12px;border-bottom:1px solid var(--fb-border-subtle, #e6e9ee)}.fb-dbg__title{margin:0 auto 0 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__steps{font-size:10px;color:var(--fb-text-muted, #667085)}.fb-dbg__current{padding:0;border:0;background:transparent;color:var(--fb-accent, #2f6feb);font:inherit;font-size:10px;cursor:pointer;text-decoration:underline}.fb-dbg__type{font-family:ui-monospace,SFMono-Regular,Menlo,monospace;font-size:9px;color:var(--fb-text-subtle, #98a2b3)}.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 });
|
|
12972
12853
|
}
|
|
12973
12854
|
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.27", ngImport: i0, type: DebugPanelComponent, decorators: [{
|
|
12974
12855
|
type: Component,
|
|
12975
|
-
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 (isStructureVariable(variable)) {\r\n <!--\r\n \u00A74.7: un'istanza non ha un letterale. Si compila un membro alla volta e si manda\r\n `className` piu' i soli membri valorizzati \u2014 la stessa forma che torna in lettura.\r\n -->\r\n <p class=\"fb-field__hint\">\r\n Istanza di <code>{{ variable.objectType }}</code>: valorizza i membri che ti servono.\r\n </p>\r\n @for (member of membersOf(variable.objectType); track member.name) {\r\n <div class=\"fb-field\">\r\n <label class=\"fb-field__label\">\r\n {{ member.name }}\r\n <span class=\"fb-dbg__type\">{{ member.dataType }}{{ member.isCollection ? '[]' : '' }}</span>\r\n </label>\r\n @if (member.dataType === 'Boolean') {\r\n <select\r\n class=\"fb-select\"\r\n (change)=\"setInputValue(variable.name + '.' + member.name, $any($event.target).value)\"\r\n >\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 if (enumValuesOf(member.objectType).length) {\r\n <!-- \u00A74.6: i valori del tipo del membro, per nome. Il numero e' solo mostrato. -->\r\n <select\r\n class=\"fb-select\"\r\n (change)=\"setInputValue(variable.name + '.' + member.name, $any($event.target).value)\"\r\n >\r\n <option value=\"\">\u2014</option>\r\n @for (entry of enumValuesOf(member.objectType); track entry.name) {\r\n <option [value]=\"entry.name\">{{ entry.label || entry.name }}</option>\r\n }\r\n </select>\r\n } @else {\r\n <input\r\n class=\"fb-input\"\r\n [type]=\"member.dataType === 'Number' || member.dataType === 'Integer' ? 'number' : 'text'\"\r\n (input)=\"setInputValue(variable.name + '.' + member.name, $any($event.target).value)\"\r\n />\r\n }\r\n </div>\r\n }\r\n @if (!membersOf(variable.objectType).length) {\r\n <p class=\"fb-field__hint\">\r\n Membri non disponibili: senza il catalogo della classe non c\u2019e\u2019 un form da generare.\r\n </p>\r\n }\r\n } @else 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 if (enumValuesOf(variable.objectType).length) {\r\n <!--\r\n \u00A74.6: un enum si passa **per nome**, e i nomi ammessi sono quelli del tipo. Senza i\r\n valori \u2014 primitiva assente o tipo senza valori \u2014 si torna alla casella di testo.\r\n -->\r\n <select class=\"fb-select\" (change)=\"setInputValue(variable.name!, $any($event.target).value)\">\r\n <option value=\"\">\u2014</option>\r\n @for (entry of enumValuesOf(variable.objectType); track entry.name) {\r\n <option [value]=\"entry.name\">{{ entry.label || entry.name }}</option>\r\n }\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"] }]
|
|
12976
|
-
}],
|
|
12856
|
+
args: [{ selector: 'fb-debug-panel', standalone: true, changeDetection: ChangeDetectionStrategy.OnPush, template: "<header class=\"fb-dbg__header\">\r\n <h2 class=\"fb-dbg__title\">Debug</h2>\r\n @if (outcome()) {\r\n <button type=\"button\" class=\"fb-btn fb-btn--ghost fb-btn--icon\" title=\"Svuota\" (click)=\"clear()\">Svuota</button>\r\n }\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 (isRunning()) {\r\n <p class=\"fb-field__hint\">Esecuzione in corso\u2026</p>\r\n }\r\n\r\n @if (!outcome() && !isRunning()) {\r\n <!--\r\n Non e\u2019 un errore: e\u2019 lo stato iniziale. Il pannello serve soprattutto ai flow senza\r\n schermate, dove non c\u2019e\u2019 nessun\u2019altra interfaccia in cui guardare la traccia.\r\n -->\r\n <p class=\"fb-field__hint\">\r\n Nessuna esecuzione. Usa <strong>Esegui</strong> o <strong>Debug</strong> nella barra in alto: qui\r\n compare cio\u2019 che l\u2019applicazione restituisce \u2014 stato, traccia degli elementi eseguiti, risorse e\r\n output.\r\n </p>\r\n }\r\n\r\n @if (isEmptyOutcome()) {\r\n <!--\r\n L\u2019ospite ha risposto, ma senza niente da mostrare. Distinto dal caso \u00ABnessuna esecuzione\u00BB\r\n perche\u2019 il rimedio e\u2019 un altro: qui manca un pezzo dell\u2019implementazione di runFlow.\r\n -->\r\n <p class=\"fb-callout\">\r\n L\u2019esecuzione e\u2019 partita, ma l\u2019applicazione non ha restituito nessun dettaglio da mostrare.\r\n </p>\r\n }\r\n\r\n @if (outcome(); as run) {\r\n @if (status() || run.steps) {\r\n <div class=\"fb-dbg__status\">\r\n @if (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]=\"status() === 'WaitingForScreen'\"\r\n >\r\n {{ statusLabel() }}\r\n </span>\r\n }\r\n @if (run.currentElementName) {\r\n <!-- Dove si e\u2019 fermata: cliccarlo porta sull\u2019elemento, come le righe della traccia. -->\r\n <button type=\"button\" class=\"fb-dbg__current\" (click)=\"elementFocused.emit(run.currentElementName!)\">\r\n su {{ run.currentElementName }}\r\n </button>\r\n }\r\n @if (run.steps) {\r\n <span class=\"fb-dbg__steps\">{{ run.steps }} passi</span>\r\n }\r\n </div>\r\n }\r\n\r\n @if (statusNote()) {\r\n <p class=\"fb-callout\">{{ statusNote() }}</p>\r\n }\r\n\r\n @if (run.fault) {\r\n <p class=\"fb-callout fb-callout--error\">{{ run.fault }}</p>\r\n }\r\n @for (message of run.errors || []; track $index) {\r\n <p class=\"fb-callout fb-callout--error\">{{ message }}</p>\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 <p class=\"fb-callout fb-callout--warn\">\r\n La traccia riporta i valori di <strong>tutte</strong> le risorse, dati personali compresi.\r\n </p>\r\n <ol class=\"fb-dbg__trace\">\r\n @for (entry of trace(); track $index) {\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 <!-- L\u2019ancora: dalla riga della traccia all\u2019elemento sul canvas. -->\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 } @else if (!wasDebug() && !isEmptyOutcome()) {\r\n <!-- Non e\u2019 un difetto: `trace` e `resources` esistono solo con `debug: true` (\u00A76.5). -->\r\n <p class=\"fb-field__hint\">\r\n Traccia e risorse arrivano solo con il comando <strong>Debug</strong>.\r\n </p>\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</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;gap:6px;padding:10px 12px;border-bottom:1px solid var(--fb-border-subtle, #e6e9ee)}.fb-dbg__title{margin:0 auto 0 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__steps{font-size:10px;color:var(--fb-text-muted, #667085)}.fb-dbg__current{padding:0;border:0;background:transparent;color:var(--fb-accent, #2f6feb);font:inherit;font-size:10px;cursor:pointer;text-decoration:underline}.fb-dbg__type{font-family:ui-monospace,SFMono-Regular,Menlo,monospace;font-size:9px;color:var(--fb-text-subtle, #98a2b3)}.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"] }]
|
|
12857
|
+
}], propDecorators: { outcome: [{ type: i0.Input, args: [{ isSignal: true, alias: "outcome", required: false }] }], wasDebug: [{ type: i0.Input, args: [{ isSignal: true, alias: "wasDebug", required: false }] }], isRunning: [{ type: i0.Input, args: [{ isSignal: true, alias: "isRunning", required: false }] }], closed: [{ type: i0.Output, args: ["closed"] }], elementFocused: [{ type: i0.Output, args: ["elementFocused"] }], cleared: [{ type: i0.Output, args: ["cleared"] }] } });
|
|
12858
|
+
function rowsOf(values) {
|
|
12859
|
+
return Object.entries(values ?? {}).map(([name, typed]) => ({
|
|
12860
|
+
name,
|
|
12861
|
+
type: typed?.type ?? '',
|
|
12862
|
+
value: render(typed),
|
|
12863
|
+
}));
|
|
12864
|
+
}
|
|
12865
|
+
/**
|
|
12866
|
+
* §6.5 — il tipo dichiarato vince, e due tipi hanno una forma propria invece di `value`: un
|
|
12867
|
+
* record e un'istanza di classe. Cercare `value` su un `Structure` mostrerebbe «—» pur avendo
|
|
12868
|
+
* i dati sotto gli occhi (§4.7).
|
|
12869
|
+
*/
|
|
12870
|
+
function render(typed) {
|
|
12871
|
+
if (!typed) {
|
|
12872
|
+
return '—';
|
|
12873
|
+
}
|
|
12874
|
+
if (typed.instance) {
|
|
12875
|
+
const members = Object.entries(typed.instance.members ?? {})
|
|
12876
|
+
.map(([name, member]) => `${name}: ${render(member)}`)
|
|
12877
|
+
.join(', ');
|
|
12878
|
+
return `${typed.instance.className ?? 'istanza'} { ${members} }`;
|
|
12879
|
+
}
|
|
12880
|
+
if (typed.record) {
|
|
12881
|
+
const fields = Object.entries(typed.record.fields ?? {})
|
|
12882
|
+
.map(([name, value]) => `${name}: ${value === null || value === undefined ? '—' : String(value)}`)
|
|
12883
|
+
.join(', ');
|
|
12884
|
+
const head = [typed.record.objectType, typed.record.id].filter(Boolean).join('#');
|
|
12885
|
+
return `${head || 'record'} { ${fields} }`;
|
|
12886
|
+
}
|
|
12887
|
+
if (typed.value === undefined || typed.value === null) {
|
|
12888
|
+
return '—';
|
|
12889
|
+
}
|
|
12890
|
+
if (typeof typed.value === 'object') {
|
|
12891
|
+
return JSON.stringify(typed.value);
|
|
12892
|
+
}
|
|
12893
|
+
return String(typed.value);
|
|
12894
|
+
}
|
|
12977
12895
|
|
|
12978
12896
|
/**
|
|
12979
12897
|
* Il builder: il componente standalone da innestare nell'applicazione ospite.
|
|
@@ -13275,9 +13193,9 @@ class FlowBuilderComponent {
|
|
|
13275
13193
|
this.isDialogOpen.set(false);
|
|
13276
13194
|
}
|
|
13277
13195
|
/**
|
|
13278
|
-
* Evidenzia l'elemento sul canvas senza cambiare pannello.
|
|
13279
|
-
* Serve
|
|
13280
|
-
*
|
|
13196
|
+
* Evidenzia l'elemento sul canvas **senza** cambiare pannello.
|
|
13197
|
+
* Serve alla traccia del debug: portare l'utente sull'elemento e' il punto del pannello, ma
|
|
13198
|
+
* sostituirlo con l'inspector farebbe sparire la traccia che si stava leggendo.
|
|
13281
13199
|
*/
|
|
13282
13200
|
highlightElement(name) {
|
|
13283
13201
|
this.selectedName.set(name);
|
|
@@ -13672,6 +13590,105 @@ class FlowBuilderComponent {
|
|
|
13672
13590
|
this.showError(error);
|
|
13673
13591
|
}
|
|
13674
13592
|
}
|
|
13593
|
+
// ------------------------------------------------------------ esegui e debug
|
|
13594
|
+
//
|
|
13595
|
+
// I due comandi non eseguono niente: raccolgono i valori di ingresso e chiamano
|
|
13596
|
+
// `runFlow`/`debugFlow`, che le implementa l'applicazione ospite. Senza implementazione la
|
|
13597
|
+
// primitiva rifiuta con `MissingService` e il banner lo dice — l'editor non finge un avvio.
|
|
13598
|
+
/** Quale delle due finestre e' aperta; `null` = nessuna. */
|
|
13599
|
+
runMode = signal(null, ...(ngDevMode ? [{ debugName: "runMode" }] : []));
|
|
13600
|
+
/** La chiamata all'ospite e' in volo: il bottone della finestra resta spento. */
|
|
13601
|
+
isStartingRun = signal(false, ...(ngDevMode ? [{ debugName: "isStartingRun" }] : []));
|
|
13602
|
+
/**
|
|
13603
|
+
* L'esito dell'ultima esecuzione, se l'ospite ne ha restituito uno.
|
|
13604
|
+
*
|
|
13605
|
+
* È tutto cio' che l'editor sa di un'esecuzione, e serve soprattutto ai flow **senza
|
|
13606
|
+
* schermate**: lì non si apre nessuna interfaccia del runtime, quindi senza questo pannello
|
|
13607
|
+
* non ci sarebbe **nessun** posto in cui vedere quali elementi sono stati eseguiti.
|
|
13608
|
+
*/
|
|
13609
|
+
runOutcome = signal(null, ...(ngDevMode ? [{ debugName: "runOutcome" }] : []));
|
|
13610
|
+
/** L'ultima esecuzione era in debug: senza, traccia e risorse non arrivano ed e' normale. */
|
|
13611
|
+
wasRunInDebug = signal(false, ...(ngDevMode ? [{ debugName: "wasRunInDebug" }] : []));
|
|
13612
|
+
/**
|
|
13613
|
+
* Si esegue cio' che e' **salvato**: su un flow mai scritto non c'e' niente da avviare, e il
|
|
13614
|
+
* titolo del bottone lo dice invece di lasciare un comando che non risponde.
|
|
13615
|
+
*/
|
|
13616
|
+
canRun = computed(() => !this.session.isNew() && !!this.session.flowName(), ...(ngDevMode ? [{ debugName: "canRun" }] : []));
|
|
13617
|
+
openRun(mode) {
|
|
13618
|
+
if (!this.canRun()) {
|
|
13619
|
+
return;
|
|
13620
|
+
}
|
|
13621
|
+
this.runMode.set(mode);
|
|
13622
|
+
}
|
|
13623
|
+
closeRun() {
|
|
13624
|
+
this.runMode.set(null);
|
|
13625
|
+
}
|
|
13626
|
+
/** Svuota il pannello: l'esito di prima non riguarda piu' il documento che si sta editando. */
|
|
13627
|
+
clearRunOutcome() {
|
|
13628
|
+
this.runOutcome.set(null);
|
|
13629
|
+
}
|
|
13630
|
+
/**
|
|
13631
|
+
* Consegna gli ingressi all'ospite.
|
|
13632
|
+
*
|
|
13633
|
+
* La finestra si chiude **subito**, prima che la chiamata risponda: un'esecuzione puo' durare
|
|
13634
|
+
* quanto vuole — con schermate da compilare puo' finire minuti dopo — e tenere aperto il form
|
|
13635
|
+
* degli ingressi per tutto quel tempo coprirebbe proprio il grafo che si sta guardando. Il
|
|
13636
|
+
* pannello «Debug» prende il posto dell'attesa, e l'esito ci arriva quando arriva.
|
|
13637
|
+
*/
|
|
13638
|
+
async confirmRun(inputs) {
|
|
13639
|
+
const mode = this.runMode();
|
|
13640
|
+
const flowName = this.session.flowName();
|
|
13641
|
+
if (!mode || !flowName) {
|
|
13642
|
+
return;
|
|
13643
|
+
}
|
|
13644
|
+
const request = {
|
|
13645
|
+
flowName,
|
|
13646
|
+
version: this.session.version() ?? undefined,
|
|
13647
|
+
inputs,
|
|
13648
|
+
debug: mode === 'debug',
|
|
13649
|
+
};
|
|
13650
|
+
this.isStartingRun.set(true);
|
|
13651
|
+
this.runOutcome.set(null);
|
|
13652
|
+
this.wasRunInDebug.set(mode === 'debug');
|
|
13653
|
+
// Il pannello e' il posto in cui si guarda cosa sta succedendo: si apre insieme all'avvio.
|
|
13654
|
+
this.activePanel.set('debug');
|
|
13655
|
+
this.runMode.set(null);
|
|
13656
|
+
try {
|
|
13657
|
+
const outcome = await (mode === 'debug' ? this.api.debugFlow(request) : this.api.runFlow(request));
|
|
13658
|
+
/**
|
|
13659
|
+
* Restituire un esito e' facoltativo: un ospite che mostra tutto in una sua interfaccia
|
|
13660
|
+
* risolve con `void`, e allora non c'e' niente da mettere nel pannello.
|
|
13661
|
+
*/
|
|
13662
|
+
this.runOutcome.set(outcome ?? null);
|
|
13663
|
+
if (!outcome) {
|
|
13664
|
+
this.notice.set({
|
|
13665
|
+
kind: 'info',
|
|
13666
|
+
message: mode === 'debug' ? 'Debug avviato.' : 'Esecuzione avviata.',
|
|
13667
|
+
});
|
|
13668
|
+
}
|
|
13669
|
+
}
|
|
13670
|
+
catch (error) {
|
|
13671
|
+
/**
|
|
13672
|
+
* `MissingService` non e' un guasto: e' un ambiente che quel comando non lo espone. Va
|
|
13673
|
+
* detto con parole sue — «il messaggio della primitiva» sarebbe «primitiva non
|
|
13674
|
+
* disponibile», che non aiuta chi ha appena premuto «Esegui».
|
|
13675
|
+
*/
|
|
13676
|
+
if (FlowApiError.is(error) && error.category === 'MissingService') {
|
|
13677
|
+
this.notice.set({
|
|
13678
|
+
kind: 'error',
|
|
13679
|
+
message: mode === 'debug'
|
|
13680
|
+
? 'Questo ambiente non espone il debug dei flow: manca l’implementazione di debugFlow.'
|
|
13681
|
+
: 'Questo ambiente non espone l’esecuzione dei flow: manca l’implementazione di runFlow.',
|
|
13682
|
+
});
|
|
13683
|
+
return;
|
|
13684
|
+
}
|
|
13685
|
+
// Un errore vero dell'ospite: si dice com'e' arrivato, senza travestirlo da esito.
|
|
13686
|
+
this.showError(error);
|
|
13687
|
+
}
|
|
13688
|
+
finally {
|
|
13689
|
+
this.isStartingRun.set(false);
|
|
13690
|
+
}
|
|
13691
|
+
}
|
|
13675
13692
|
async createNewVersion() {
|
|
13676
13693
|
try {
|
|
13677
13694
|
const result = await this.session.createNewVersion();
|
|
@@ -13825,11 +13842,12 @@ class FlowBuilderComponent {
|
|
|
13825
13842
|
FormulaValidationService,
|
|
13826
13843
|
FlowEditorSession,
|
|
13827
13844
|
FlowLayoutService,
|
|
13828
|
-
], viewQueries: [{ propertyName: "copyNameInput", first: true, predicate: ["copyNameInput"], descendants: true, isSignal: true }], 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 <!--\r\n Il segnaposto esiste perche' un `<select>` senza opzione corrispondente non e'\r\n \u00ABvuoto\u00BB: e' a `selectedIndex = -1`, e mostra una casella bianca. Su un flow nuovo\r\n (`processType` non ancora scelto) e' questa la riga che si vede, disabilitata\r\n perche' non e' un valore valido da salvare.\r\n -->\r\n @if (!document().processType) {\r\n <option value=\"\" disabled>\u2014 tipo di flow \u2014</option>\r\n }\r\n @for (type of processTypes(); track type.value) {\r\n <option [value]=\"type.value\">{{ type.label }}</option>\r\n }\r\n <!--\r\n Il tipo del documento che il dizionario non conosce: senza questa opzione la select\r\n resta bianca su un flow che il backend ha salvato con un `processType` fuori\r\n catalogo (o quando `processTypes` manca dalla risposta dei dizionari), e sembra che\r\n l'editor non abbia ricaricato il flow. Va mostrato **senza** riscrivere il\r\n documento: il valore e' del backend, non nostro da correggere.\r\n -->\r\n @if (isProcessTypeOutOfCatalog()) {\r\n <option [value]=\"document().processType\">{{ document().processType }} (fuori catalogo)</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\r\n <!--\r\n Copia e incolla stanno **anche** qui e non solo sui tasti: una scorciatoia che nessuno\r\n annuncia non esiste. Il titolo la dice, cos\u00EC si impara usandola una volta.\r\n L\u2019incolla e\u2019 acceso anche con la selezione vuota: cio\u2019 che si incolla sta negli appunti,\r\n e puo\u2019 venire da un altro flow.\r\n -->\r\n <button\r\n type=\"button\"\r\n class=\"fb-btn\"\r\n [disabled]=\"!canCopy()\"\r\n title=\"Copia gli elementi selezionati, con le risorse che usano (Ctrl+C)\"\r\n aria-label=\"Copia gli elementi selezionati\"\r\n (click)=\"copySelection()\"\r\n >\r\n \u29C9\r\n </button>\r\n <button\r\n type=\"button\"\r\n class=\"fb-btn\"\r\n [disabled]=\"!canPaste() || !isEditable()\"\r\n title=\"Incolla gli elementi copiati, anche da un altro flow (Ctrl+V)\"\r\n aria-label=\"Incolla gli elementi copiati\"\r\n (click)=\"startPaste()\"\r\n >\r\n \u2398\r\n </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 @if (canDuplicate()) {\r\n <!-- \u00A76.2 \u00ABDuplica\u00BB: il flow sotto un altro nome, alla versione 1. -->\r\n <button\r\n type=\"button\"\r\n class=\"fb-btn\"\r\n [disabled]=\"isBusy()\"\r\n title=\"Salva una copia con un altro nome\"\r\n (click)=\"startCopy()\"\r\n >\r\n Duplica\u2026\r\n </button>\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 (isCopyOpen()) {\r\n <!--\r\n Il nome si chiede prima di scrivere: e\u2019 l\u2019unico dato che il backend non puo\u2019 inventare, e\r\n un doppione lo rifiuta con AlreadyExists (\u00A76.2). Invio conferma, Esc annulla.\r\n -->\r\n <div class=\"fb-banner fb-copy\" role=\"group\" aria-label=\"Duplica il flow\">\r\n <label class=\"fb-copy__field\">\r\n <span class=\"fb-copy__caption\">Nome tecnico della copia</span>\r\n <input\r\n #copyNameInput\r\n class=\"fb-copy__input\"\r\n [value]=\"copyName()\"\r\n placeholder=\"NomeTecnico_Copia\"\r\n (input)=\"setCopyName($any($event.target).value)\"\r\n (keydown.enter)=\"confirmCopy()\"\r\n (keydown.escape)=\"cancelCopy()\"\r\n />\r\n </label>\r\n <label class=\"fb-copy__field\">\r\n <span class=\"fb-copy__caption\">Nome visibile</span>\r\n <input\r\n class=\"fb-copy__input\"\r\n [value]=\"copyLabel()\"\r\n placeholder=\"(facoltativo)\"\r\n (input)=\"setCopyLabel($any($event.target).value)\"\r\n (keydown.enter)=\"confirmCopy()\"\r\n (keydown.escape)=\"cancelCopy()\"\r\n />\r\n </label>\r\n <span class=\"fb-copy__hint\" [class.fb-copy__hint--error]=\"!!copyNameProblem()\">\r\n {{ copyNameProblem() || copyHint() }}\r\n </span>\r\n <button\r\n type=\"button\"\r\n class=\"fb-btn fb-btn--primary fb-btn--icon\"\r\n [disabled]=\"isBusy() || !!copyNameProblem()\"\r\n (click)=\"confirmCopy()\"\r\n >\r\n Duplica\r\n </button>\r\n <button type=\"button\" class=\"fb-btn fb-btn--ghost fb-btn--icon\" (click)=\"cancelCopy()\">Annulla</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 [selectedNames]=\"selectedNames()\"\r\n [outline]=\"outline()\"\r\n [isEditable]=\"isEditable()\"\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 (renamed)=\"onNodeRenamed($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 (pastePreview(); as preview) {\r\n <!--\r\n L\u2019incolla passa da una conferma perche\u2019 non e\u2019 mai una copia identica: nomi gi\u00E0 presi,\r\n risorse che qui non esistono, riferimenti che restano orfani. Il piano e\u2019 gi\u00E0 calcolato,\r\n la finestra lo mostra e basta.\r\n -->\r\n <fb-paste-dialog\r\n [payload]=\"preview.payload\"\r\n [plan]=\"preview.plan\"\r\n (confirmed)=\"confirmPaste($event)\"\r\n (cancelled)=\"cancelPaste()\"\r\n />\r\n }\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 (renamed)=\"onNodeRenamed($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-copy__field{display:flex;align-items:center;gap:6px}.fb-copy__caption{color:var(--fb-text-muted, #667085);white-space:nowrap}.fb-copy__input{width:180px;padding:2px 5px;border:1px solid var(--fb-border, #d6dae1);border-radius:4px;background:var(--fb-surface, #fff);color:var(--fb-text, #1d2939);font-family:ui-monospace,SFMono-Regular,Menlo,monospace;font-size:11px}.fb-copy__hint{flex:1;min-width:180px;color:var(--fb-text-muted, #667085)}.fb-copy__hint--error{color:var(--fb-error, #c9372c)}.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", "renamed"] }, { kind: "component", type: ElementInspectorComponent, selector: "fb-element-inspector", inputs: ["selectedName", "showHeader"], outputs: ["closed", "removeRequested", "duplicateRequested", "renamed"] }, { kind: "component", type: ElementPaletteComponent, selector: "fb-element-palette", inputs: ["processType"], outputs: ["elementPicked"] }, { kind: "component", type: FlowCanvasComponent, selector: "fb-flow-canvas", inputs: ["selectedName", "selectedNames", "outline", "isEditable"], outputs: ["selectionChange", "nodeOpened", "nodeRemoveRequested", "nodeDuplicateRequested", "elementDropped"] }, { kind: "component", type: PasteDialogComponent, selector: "fb-paste-dialog", inputs: ["payload", "plan"], outputs: ["confirmed", "cancelled"] }, { 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 });
|
|
13845
|
+
], viewQueries: [{ propertyName: "copyNameInput", first: true, predicate: ["copyNameInput"], descendants: true, isSignal: true }], 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 <!--\r\n Il segnaposto esiste perche' un `<select>` senza opzione corrispondente non e'\r\n \u00ABvuoto\u00BB: e' a `selectedIndex = -1`, e mostra una casella bianca. Su un flow nuovo\r\n (`processType` non ancora scelto) e' questa la riga che si vede, disabilitata\r\n perche' non e' un valore valido da salvare.\r\n -->\r\n @if (!document().processType) {\r\n <option value=\"\" disabled>\u2014 tipo di flow \u2014</option>\r\n }\r\n @for (type of processTypes(); track type.value) {\r\n <option [value]=\"type.value\">{{ type.label }}</option>\r\n }\r\n <!--\r\n Il tipo del documento che il dizionario non conosce: senza questa opzione la select\r\n resta bianca su un flow che il backend ha salvato con un `processType` fuori\r\n catalogo (o quando `processTypes` manca dalla risposta dei dizionari), e sembra che\r\n l'editor non abbia ricaricato il flow. Va mostrato **senza** riscrivere il\r\n documento: il valore e' del backend, non nostro da correggere.\r\n -->\r\n @if (isProcessTypeOutOfCatalog()) {\r\n <option [value]=\"document().processType\">{{ document().processType }} (fuori catalogo)</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\r\n <!--\r\n Copia e incolla stanno **anche** qui e non solo sui tasti: una scorciatoia che nessuno\r\n annuncia non esiste. Il titolo la dice, cos\u00EC si impara usandola una volta.\r\n L\u2019incolla e\u2019 acceso anche con la selezione vuota: cio\u2019 che si incolla sta negli appunti,\r\n e puo\u2019 venire da un altro flow.\r\n -->\r\n <button\r\n type=\"button\"\r\n class=\"fb-btn\"\r\n [disabled]=\"!canCopy()\"\r\n title=\"Copia gli elementi selezionati, con le risorse che usano (Ctrl+C)\"\r\n aria-label=\"Copia gli elementi selezionati\"\r\n (click)=\"copySelection()\"\r\n >\r\n \u29C9\r\n </button>\r\n <button\r\n type=\"button\"\r\n class=\"fb-btn\"\r\n [disabled]=\"!canPaste() || !isEditable()\"\r\n title=\"Incolla gli elementi copiati, anche da un altro flow (Ctrl+V)\"\r\n aria-label=\"Incolla gli elementi copiati\"\r\n (click)=\"startPaste()\"\r\n >\r\n \u2398\r\n </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 <!--\r\n Esegui e Debug non eseguono niente qui dentro: raccolgono i valori di ingresso e li\r\n consegnano all\u2019applicazione ospite. Si esegue la versione **salvata**, quindi su un flow\r\n mai scritto sono spenti e il titolo dice perche\u2019.\r\n -->\r\n <button\r\n type=\"button\"\r\n class=\"fb-btn\"\r\n [disabled]=\"!canRun()\"\r\n [title]=\"canRun() ? 'Esegui il flow salvato' : 'Salva il flow prima di eseguirlo'\"\r\n (click)=\"openRun('run')\"\r\n >\r\n Esegui\r\n </button>\r\n <button\r\n type=\"button\"\r\n class=\"fb-btn\"\r\n [disabled]=\"!canRun()\"\r\n [title]=\"canRun() ? 'Esegui il flow salvato in debug' : 'Salva il flow prima di eseguirlo'\"\r\n (click)=\"openRun('debug')\"\r\n >\r\n Debug\r\n </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 @if (canDuplicate()) {\r\n <!-- \u00A76.2 \u00ABDuplica\u00BB: il flow sotto un altro nome, alla versione 1. -->\r\n <button\r\n type=\"button\"\r\n class=\"fb-btn\"\r\n [disabled]=\"isBusy()\"\r\n title=\"Salva una copia con un altro nome\"\r\n (click)=\"startCopy()\"\r\n >\r\n Duplica\u2026\r\n </button>\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 (isCopyOpen()) {\r\n <!--\r\n Il nome si chiede prima di scrivere: e\u2019 l\u2019unico dato che il backend non puo\u2019 inventare, e\r\n un doppione lo rifiuta con AlreadyExists (\u00A76.2). Invio conferma, Esc annulla.\r\n -->\r\n <div class=\"fb-banner fb-copy\" role=\"group\" aria-label=\"Duplica il flow\">\r\n <label class=\"fb-copy__field\">\r\n <span class=\"fb-copy__caption\">Nome tecnico della copia</span>\r\n <input\r\n #copyNameInput\r\n class=\"fb-copy__input\"\r\n [value]=\"copyName()\"\r\n placeholder=\"NomeTecnico_Copia\"\r\n (input)=\"setCopyName($any($event.target).value)\"\r\n (keydown.enter)=\"confirmCopy()\"\r\n (keydown.escape)=\"cancelCopy()\"\r\n />\r\n </label>\r\n <label class=\"fb-copy__field\">\r\n <span class=\"fb-copy__caption\">Nome visibile</span>\r\n <input\r\n class=\"fb-copy__input\"\r\n [value]=\"copyLabel()\"\r\n placeholder=\"(facoltativo)\"\r\n (input)=\"setCopyLabel($any($event.target).value)\"\r\n (keydown.enter)=\"confirmCopy()\"\r\n (keydown.escape)=\"cancelCopy()\"\r\n />\r\n </label>\r\n <span class=\"fb-copy__hint\" [class.fb-copy__hint--error]=\"!!copyNameProblem()\">\r\n {{ copyNameProblem() || copyHint() }}\r\n </span>\r\n <button\r\n type=\"button\"\r\n class=\"fb-btn fb-btn--primary fb-btn--icon\"\r\n [disabled]=\"isBusy() || !!copyNameProblem()\"\r\n (click)=\"confirmCopy()\"\r\n >\r\n Duplica\r\n </button>\r\n <button type=\"button\" class=\"fb-btn fb-btn--ghost fb-btn--icon\" (click)=\"cancelCopy()\">Annulla</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 [selectedNames]=\"selectedNames()\"\r\n [outline]=\"outline()\"\r\n [isEditable]=\"isEditable()\"\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 <!--\r\n Il pannello dell\u2019ultima esecuzione. \u00C8 l\u2019unico posto in cui si vede la traccia di un\r\n flow **senza schermate**: l\u00EC il runtime non apre nessuna interfaccia, e senza questo\r\n tab non ci sarebbe niente da guardare.\r\n -->\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 Debug\r\n @if (isStartingRun()) {\r\n <span class=\"fb-side__tab-dot\" aria-label=\"esecuzione in corso\"></span>\r\n }\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 (renamed)=\"onNodeRenamed($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: la traccia si sta leggendo. -->\r\n <fb-debug-panel\r\n [outcome]=\"runOutcome()\"\r\n [wasDebug]=\"wasRunInDebug()\"\r\n [isRunning]=\"isStartingRun()\"\r\n (elementFocused)=\"highlightElement($event)\"\r\n (cleared)=\"clearRunOutcome()\"\r\n (closed)=\"setPanel('inspector')\"\r\n />\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 (pastePreview(); as preview) {\r\n <!--\r\n L\u2019incolla passa da una conferma perche\u2019 non e\u2019 mai una copia identica: nomi gi\u00E0 presi,\r\n risorse che qui non esistono, riferimenti che restano orfani. Il piano e\u2019 gi\u00E0 calcolato,\r\n la finestra lo mostra e basta.\r\n -->\r\n <fb-paste-dialog\r\n [payload]=\"preview.payload\"\r\n [plan]=\"preview.plan\"\r\n (confirmed)=\"confirmPaste($event)\"\r\n (cancelled)=\"cancelPaste()\"\r\n />\r\n }\r\n\r\n @if (runMode(); as mode) {\r\n <!--\r\n La finestra degli ingressi. Non avvia niente: raccoglie i valori e li passa all\u2019ospite,\r\n che e\u2019 l\u2019unico a sapere dove gira il motore.\r\n -->\r\n <fb-run-dialog\r\n [mode]=\"mode\"\r\n [flowName]=\"session.flowName()\"\r\n [version]=\"session.version()\"\r\n [isDirty]=\"isDirty()\"\r\n [isBusy]=\"isStartingRun()\"\r\n (confirmed)=\"confirmRun($event)\"\r\n (cancelled)=\"closeRun()\"\r\n />\r\n }\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 (renamed)=\"onNodeRenamed($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-copy__field{display:flex;align-items:center;gap:6px}.fb-copy__caption{color:var(--fb-text-muted, #667085);white-space:nowrap}.fb-copy__input{width:180px;padding:2px 5px;border:1px solid var(--fb-border, #d6dae1);border-radius:4px;background:var(--fb-surface, #fff);color:var(--fb-text, #1d2939);font-family:ui-monospace,SFMono-Regular,Menlo,monospace;font-size:11px}.fb-copy__hint{flex:1;min-width:180px;color:var(--fb-text-muted, #667085)}.fb-copy__hint--error{color:var(--fb-error, #c9372c)}.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-dot{display:inline-block;width:6px;height:6px;margin-left:4px;border-radius:50%;background:var(--fb-accent, #2f6feb);vertical-align:middle}.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", inputs: ["outcome", "wasDebug", "isRunning"], outputs: ["closed", "elementFocused", "cleared"] }, { kind: "component", type: ElementDialogComponent, selector: "fb-element-dialog", inputs: ["selectedName"], outputs: ["closed", "removeRequested", "duplicateRequested", "renamed"] }, { kind: "component", type: ElementInspectorComponent, selector: "fb-element-inspector", inputs: ["selectedName", "showHeader"], outputs: ["closed", "removeRequested", "duplicateRequested", "renamed"] }, { kind: "component", type: ElementPaletteComponent, selector: "fb-element-palette", inputs: ["processType"], outputs: ["elementPicked"] }, { kind: "component", type: FlowCanvasComponent, selector: "fb-flow-canvas", inputs: ["selectedName", "selectedNames", "outline", "isEditable"], outputs: ["selectionChange", "nodeOpened", "nodeRemoveRequested", "nodeDuplicateRequested", "elementDropped"] }, { kind: "component", type: PasteDialogComponent, selector: "fb-paste-dialog", inputs: ["payload", "plan"], outputs: ["confirmed", "cancelled"] }, { kind: "component", type: ProblemsPanelComponent, selector: "fb-problems-panel", outputs: ["elementFocused", "closed"] }, { kind: "component", type: ResourcePanelComponent, selector: "fb-resource-panel", outputs: ["closed"] }, { kind: "component", type: RunDialogComponent, selector: "fb-run-dialog", inputs: ["mode", "flowName", "version", "isDirty", "isBusy"], outputs: ["confirmed", "cancelled"] }, { 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 });
|
|
13829
13846
|
}
|
|
13830
13847
|
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.27", ngImport: i0, type: FlowBuilderComponent, decorators: [{
|
|
13831
13848
|
type: Component,
|
|
13832
|
-
args: [{ selector: 'fb-flow-builder', standalone: true, imports: [
|
|
13849
|
+
args: [{ selector: 'fb-flow-builder', standalone: true, imports: [
|
|
13850
|
+
DebugPanelComponent,
|
|
13833
13851
|
ElementDialogComponent,
|
|
13834
13852
|
ElementInspectorComponent,
|
|
13835
13853
|
ElementPaletteComponent,
|
|
@@ -13837,7 +13855,9 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.27", ngImpo
|
|
|
13837
13855
|
PasteDialogComponent,
|
|
13838
13856
|
ProblemsPanelComponent,
|
|
13839
13857
|
ResourcePanelComponent,
|
|
13840
|
-
|
|
13858
|
+
RunDialogComponent,
|
|
13859
|
+
VersionPanelComponent, SelectValueDirective
|
|
13860
|
+
], providers: [
|
|
13841
13861
|
FlowDocumentStore,
|
|
13842
13862
|
FlowDictionaryStore,
|
|
13843
13863
|
FlowCatalogStore,
|
|
@@ -13845,12 +13865,12 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.27", ngImpo
|
|
|
13845
13865
|
FormulaValidationService,
|
|
13846
13866
|
FlowEditorSession,
|
|
13847
13867
|
FlowLayoutService,
|
|
13848
|
-
], changeDetection: ChangeDetectionStrategy.OnPush, host: { '(keydown)': 'onKeyDown($event)' }, 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 <!--\r\n Il segnaposto esiste perche' un `<select>` senza opzione corrispondente non e'\r\n \u00ABvuoto\u00BB: e' a `selectedIndex = -1`, e mostra una casella bianca. Su un flow nuovo\r\n (`processType` non ancora scelto) e' questa la riga che si vede, disabilitata\r\n perche' non e' un valore valido da salvare.\r\n -->\r\n @if (!document().processType) {\r\n <option value=\"\" disabled>\u2014 tipo di flow \u2014</option>\r\n }\r\n @for (type of processTypes(); track type.value) {\r\n <option [value]=\"type.value\">{{ type.label }}</option>\r\n }\r\n <!--\r\n Il tipo del documento che il dizionario non conosce: senza questa opzione la select\r\n resta bianca su un flow che il backend ha salvato con un `processType` fuori\r\n catalogo (o quando `processTypes` manca dalla risposta dei dizionari), e sembra che\r\n l'editor non abbia ricaricato il flow. Va mostrato **senza** riscrivere il\r\n documento: il valore e' del backend, non nostro da correggere.\r\n -->\r\n @if (isProcessTypeOutOfCatalog()) {\r\n <option [value]=\"document().processType\">{{ document().processType }} (fuori catalogo)</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\r\n <!--\r\n Copia e incolla stanno **anche** qui e non solo sui tasti: una scorciatoia che nessuno\r\n annuncia non esiste. Il titolo la dice, cos\u00EC si impara usandola una volta.\r\n L\u2019incolla e\u2019 acceso anche con la selezione vuota: cio\u2019 che si incolla sta negli appunti,\r\n e puo\u2019 venire da un altro flow.\r\n -->\r\n <button\r\n type=\"button\"\r\n class=\"fb-btn\"\r\n [disabled]=\"!canCopy()\"\r\n title=\"Copia gli elementi selezionati, con le risorse che usano (Ctrl+C)\"\r\n aria-label=\"Copia gli elementi selezionati\"\r\n (click)=\"copySelection()\"\r\n >\r\n \u29C9\r\n </button>\r\n <button\r\n type=\"button\"\r\n class=\"fb-btn\"\r\n [disabled]=\"!canPaste() || !isEditable()\"\r\n title=\"Incolla gli elementi copiati, anche da un altro flow (Ctrl+V)\"\r\n aria-label=\"Incolla gli elementi copiati\"\r\n (click)=\"startPaste()\"\r\n >\r\n \u2398\r\n </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 @if (canDuplicate()) {\r\n <!-- \u00A76.2 \u00ABDuplica\u00BB: il flow sotto un altro nome, alla versione 1. -->\r\n <button\r\n type=\"button\"\r\n class=\"fb-btn\"\r\n [disabled]=\"isBusy()\"\r\n title=\"Salva una copia con un altro nome\"\r\n (click)=\"startCopy()\"\r\n >\r\n Duplica\u2026\r\n </button>\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 (isCopyOpen()) {\r\n <!--\r\n Il nome si chiede prima di scrivere: e\u2019 l\u2019unico dato che il backend non puo\u2019 inventare, e\r\n un doppione lo rifiuta con AlreadyExists (\u00A76.2). Invio conferma, Esc annulla.\r\n -->\r\n <div class=\"fb-banner fb-copy\" role=\"group\" aria-label=\"Duplica il flow\">\r\n <label class=\"fb-copy__field\">\r\n <span class=\"fb-copy__caption\">Nome tecnico della copia</span>\r\n <input\r\n #copyNameInput\r\n class=\"fb-copy__input\"\r\n [value]=\"copyName()\"\r\n placeholder=\"NomeTecnico_Copia\"\r\n (input)=\"setCopyName($any($event.target).value)\"\r\n (keydown.enter)=\"confirmCopy()\"\r\n (keydown.escape)=\"cancelCopy()\"\r\n />\r\n </label>\r\n <label class=\"fb-copy__field\">\r\n <span class=\"fb-copy__caption\">Nome visibile</span>\r\n <input\r\n class=\"fb-copy__input\"\r\n [value]=\"copyLabel()\"\r\n placeholder=\"(facoltativo)\"\r\n (input)=\"setCopyLabel($any($event.target).value)\"\r\n (keydown.enter)=\"confirmCopy()\"\r\n (keydown.escape)=\"cancelCopy()\"\r\n />\r\n </label>\r\n <span class=\"fb-copy__hint\" [class.fb-copy__hint--error]=\"!!copyNameProblem()\">\r\n {{ copyNameProblem() || copyHint() }}\r\n </span>\r\n <button\r\n type=\"button\"\r\n class=\"fb-btn fb-btn--primary fb-btn--icon\"\r\n [disabled]=\"isBusy() || !!copyNameProblem()\"\r\n (click)=\"confirmCopy()\"\r\n >\r\n Duplica\r\n </button>\r\n <button type=\"button\" class=\"fb-btn fb-btn--ghost fb-btn--icon\" (click)=\"cancelCopy()\">Annulla</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 [selectedNames]=\"selectedNames()\"\r\n [outline]=\"outline()\"\r\n [isEditable]=\"isEditable()\"\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 (renamed)=\"onNodeRenamed($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 (pastePreview(); as preview) {\r\n <!--\r\n L\u2019incolla passa da una conferma perche\u2019 non e\u2019 mai una copia identica: nomi gi\u00E0 presi,\r\n risorse che qui non esistono, riferimenti che restano orfani. Il piano e\u2019 gi\u00E0 calcolato,\r\n la finestra lo mostra e basta.\r\n -->\r\n <fb-paste-dialog\r\n [payload]=\"preview.payload\"\r\n [plan]=\"preview.plan\"\r\n (confirmed)=\"confirmPaste($event)\"\r\n (cancelled)=\"cancelPaste()\"\r\n />\r\n }\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 (renamed)=\"onNodeRenamed($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-copy__field{display:flex;align-items:center;gap:6px}.fb-copy__caption{color:var(--fb-text-muted, #667085);white-space:nowrap}.fb-copy__input{width:180px;padding:2px 5px;border:1px solid var(--fb-border, #d6dae1);border-radius:4px;background:var(--fb-surface, #fff);color:var(--fb-text, #1d2939);font-family:ui-monospace,SFMono-Regular,Menlo,monospace;font-size:11px}.fb-copy__hint{flex:1;min-width:180px;color:var(--fb-text-muted, #667085)}.fb-copy__hint--error{color:var(--fb-error, #c9372c)}.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"] }]
|
|
13868
|
+
], changeDetection: ChangeDetectionStrategy.OnPush, host: { '(keydown)': 'onKeyDown($event)' }, 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 <!--\r\n Il segnaposto esiste perche' un `<select>` senza opzione corrispondente non e'\r\n \u00ABvuoto\u00BB: e' a `selectedIndex = -1`, e mostra una casella bianca. Su un flow nuovo\r\n (`processType` non ancora scelto) e' questa la riga che si vede, disabilitata\r\n perche' non e' un valore valido da salvare.\r\n -->\r\n @if (!document().processType) {\r\n <option value=\"\" disabled>\u2014 tipo di flow \u2014</option>\r\n }\r\n @for (type of processTypes(); track type.value) {\r\n <option [value]=\"type.value\">{{ type.label }}</option>\r\n }\r\n <!--\r\n Il tipo del documento che il dizionario non conosce: senza questa opzione la select\r\n resta bianca su un flow che il backend ha salvato con un `processType` fuori\r\n catalogo (o quando `processTypes` manca dalla risposta dei dizionari), e sembra che\r\n l'editor non abbia ricaricato il flow. Va mostrato **senza** riscrivere il\r\n documento: il valore e' del backend, non nostro da correggere.\r\n -->\r\n @if (isProcessTypeOutOfCatalog()) {\r\n <option [value]=\"document().processType\">{{ document().processType }} (fuori catalogo)</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\r\n <!--\r\n Copia e incolla stanno **anche** qui e non solo sui tasti: una scorciatoia che nessuno\r\n annuncia non esiste. Il titolo la dice, cos\u00EC si impara usandola una volta.\r\n L\u2019incolla e\u2019 acceso anche con la selezione vuota: cio\u2019 che si incolla sta negli appunti,\r\n e puo\u2019 venire da un altro flow.\r\n -->\r\n <button\r\n type=\"button\"\r\n class=\"fb-btn\"\r\n [disabled]=\"!canCopy()\"\r\n title=\"Copia gli elementi selezionati, con le risorse che usano (Ctrl+C)\"\r\n aria-label=\"Copia gli elementi selezionati\"\r\n (click)=\"copySelection()\"\r\n >\r\n \u29C9\r\n </button>\r\n <button\r\n type=\"button\"\r\n class=\"fb-btn\"\r\n [disabled]=\"!canPaste() || !isEditable()\"\r\n title=\"Incolla gli elementi copiati, anche da un altro flow (Ctrl+V)\"\r\n aria-label=\"Incolla gli elementi copiati\"\r\n (click)=\"startPaste()\"\r\n >\r\n \u2398\r\n </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 <!--\r\n Esegui e Debug non eseguono niente qui dentro: raccolgono i valori di ingresso e li\r\n consegnano all\u2019applicazione ospite. Si esegue la versione **salvata**, quindi su un flow\r\n mai scritto sono spenti e il titolo dice perche\u2019.\r\n -->\r\n <button\r\n type=\"button\"\r\n class=\"fb-btn\"\r\n [disabled]=\"!canRun()\"\r\n [title]=\"canRun() ? 'Esegui il flow salvato' : 'Salva il flow prima di eseguirlo'\"\r\n (click)=\"openRun('run')\"\r\n >\r\n Esegui\r\n </button>\r\n <button\r\n type=\"button\"\r\n class=\"fb-btn\"\r\n [disabled]=\"!canRun()\"\r\n [title]=\"canRun() ? 'Esegui il flow salvato in debug' : 'Salva il flow prima di eseguirlo'\"\r\n (click)=\"openRun('debug')\"\r\n >\r\n Debug\r\n </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 @if (canDuplicate()) {\r\n <!-- \u00A76.2 \u00ABDuplica\u00BB: il flow sotto un altro nome, alla versione 1. -->\r\n <button\r\n type=\"button\"\r\n class=\"fb-btn\"\r\n [disabled]=\"isBusy()\"\r\n title=\"Salva una copia con un altro nome\"\r\n (click)=\"startCopy()\"\r\n >\r\n Duplica\u2026\r\n </button>\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 (isCopyOpen()) {\r\n <!--\r\n Il nome si chiede prima di scrivere: e\u2019 l\u2019unico dato che il backend non puo\u2019 inventare, e\r\n un doppione lo rifiuta con AlreadyExists (\u00A76.2). Invio conferma, Esc annulla.\r\n -->\r\n <div class=\"fb-banner fb-copy\" role=\"group\" aria-label=\"Duplica il flow\">\r\n <label class=\"fb-copy__field\">\r\n <span class=\"fb-copy__caption\">Nome tecnico della copia</span>\r\n <input\r\n #copyNameInput\r\n class=\"fb-copy__input\"\r\n [value]=\"copyName()\"\r\n placeholder=\"NomeTecnico_Copia\"\r\n (input)=\"setCopyName($any($event.target).value)\"\r\n (keydown.enter)=\"confirmCopy()\"\r\n (keydown.escape)=\"cancelCopy()\"\r\n />\r\n </label>\r\n <label class=\"fb-copy__field\">\r\n <span class=\"fb-copy__caption\">Nome visibile</span>\r\n <input\r\n class=\"fb-copy__input\"\r\n [value]=\"copyLabel()\"\r\n placeholder=\"(facoltativo)\"\r\n (input)=\"setCopyLabel($any($event.target).value)\"\r\n (keydown.enter)=\"confirmCopy()\"\r\n (keydown.escape)=\"cancelCopy()\"\r\n />\r\n </label>\r\n <span class=\"fb-copy__hint\" [class.fb-copy__hint--error]=\"!!copyNameProblem()\">\r\n {{ copyNameProblem() || copyHint() }}\r\n </span>\r\n <button\r\n type=\"button\"\r\n class=\"fb-btn fb-btn--primary fb-btn--icon\"\r\n [disabled]=\"isBusy() || !!copyNameProblem()\"\r\n (click)=\"confirmCopy()\"\r\n >\r\n Duplica\r\n </button>\r\n <button type=\"button\" class=\"fb-btn fb-btn--ghost fb-btn--icon\" (click)=\"cancelCopy()\">Annulla</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 [selectedNames]=\"selectedNames()\"\r\n [outline]=\"outline()\"\r\n [isEditable]=\"isEditable()\"\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 <!--\r\n Il pannello dell\u2019ultima esecuzione. \u00C8 l\u2019unico posto in cui si vede la traccia di un\r\n flow **senza schermate**: l\u00EC il runtime non apre nessuna interfaccia, e senza questo\r\n tab non ci sarebbe niente da guardare.\r\n -->\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 Debug\r\n @if (isStartingRun()) {\r\n <span class=\"fb-side__tab-dot\" aria-label=\"esecuzione in corso\"></span>\r\n }\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 (renamed)=\"onNodeRenamed($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: la traccia si sta leggendo. -->\r\n <fb-debug-panel\r\n [outcome]=\"runOutcome()\"\r\n [wasDebug]=\"wasRunInDebug()\"\r\n [isRunning]=\"isStartingRun()\"\r\n (elementFocused)=\"highlightElement($event)\"\r\n (cleared)=\"clearRunOutcome()\"\r\n (closed)=\"setPanel('inspector')\"\r\n />\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 (pastePreview(); as preview) {\r\n <!--\r\n L\u2019incolla passa da una conferma perche\u2019 non e\u2019 mai una copia identica: nomi gi\u00E0 presi,\r\n risorse che qui non esistono, riferimenti che restano orfani. Il piano e\u2019 gi\u00E0 calcolato,\r\n la finestra lo mostra e basta.\r\n -->\r\n <fb-paste-dialog\r\n [payload]=\"preview.payload\"\r\n [plan]=\"preview.plan\"\r\n (confirmed)=\"confirmPaste($event)\"\r\n (cancelled)=\"cancelPaste()\"\r\n />\r\n }\r\n\r\n @if (runMode(); as mode) {\r\n <!--\r\n La finestra degli ingressi. Non avvia niente: raccoglie i valori e li passa all\u2019ospite,\r\n che e\u2019 l\u2019unico a sapere dove gira il motore.\r\n -->\r\n <fb-run-dialog\r\n [mode]=\"mode\"\r\n [flowName]=\"session.flowName()\"\r\n [version]=\"session.version()\"\r\n [isDirty]=\"isDirty()\"\r\n [isBusy]=\"isStartingRun()\"\r\n (confirmed)=\"confirmRun($event)\"\r\n (cancelled)=\"closeRun()\"\r\n />\r\n }\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 (renamed)=\"onNodeRenamed($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-copy__field{display:flex;align-items:center;gap:6px}.fb-copy__caption{color:var(--fb-text-muted, #667085);white-space:nowrap}.fb-copy__input{width:180px;padding:2px 5px;border:1px solid var(--fb-border, #d6dae1);border-radius:4px;background:var(--fb-surface, #fff);color:var(--fb-text, #1d2939);font-family:ui-monospace,SFMono-Regular,Menlo,monospace;font-size:11px}.fb-copy__hint{flex:1;min-width:180px;color:var(--fb-text-muted, #667085)}.fb-copy__hint--error{color:var(--fb-error, #c9372c)}.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-dot{display:inline-block;width:6px;height:6px;margin-left:4px;border-radius:50%;background:var(--fb-accent, #2f6feb);vertical-align:middle}.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"] }]
|
|
13849
13869
|
}], 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"] }], copyNameInput: [{ type: i0.ViewChild, args: ['copyNameInput', { isSignal: true }] }] } });
|
|
13850
13870
|
|
|
13851
13871
|
/**
|
|
13852
13872
|
* Generated bundle index. Do not edit.
|
|
13853
13873
|
*/
|
|
13854
13874
|
|
|
13855
|
-
export { ConditionEditorComponent, ConnectorEditorComponent, DebugPanelComponent, DynamicScreenInspectorComponent, ElementDialogComponent, ElementInspectorComponent, ElementPaletteComponent, EnumValuePickerComponent, FALLBACK_COLLECTION_BY_TYPE, FALLBACK_TYPE_LABEL, FLOW_BUILDER_HTTP_CONFIG, FLOW_CLIPBOARD_KIND, FLOW_CLIPBOARD_VERSION, FLOW_ELEMENT_ICONS, FLOW_ELEMENT_VARIANT_FIELDS, FLOW_ELEMENT_VARIANT_ICONS, FLOW_ERROR_FALLBACK_MESSAGE, FLOW_ERROR_HTTP_STATUS, FLOW_NAME_PATTERN, FLOW_NODE_COLLECTIONS, FLOW_NODE_HEIGHT, FLOW_NODE_WIDTH, FLOW_REFERENCE_FIELDS, FLOW_RESOURCE_COLLECTIONS, FLOW_VALUE_FIELDS, FLOW_VALUE_LITERAL_FIELDS, FieldAssignmentEditorComponent, FieldPickerComponent, FlowApiError, FlowBuilderApi, FlowBuilderComponent, FlowCanvasComponent, FlowCatalogStore, FlowClipboardService, FlowDictionaryStore, FlowDocumentStore, FlowEditorSession, FlowLayoutService, FlowValidationStore, FormulaEditorComponent, FormulaValidationService, HttpFlowBuilderApi, NamePickerComponent, NodeInspectorBase, ORCHESTRATION_CONDITION_OUTPUT, ObjectPickerComponent, OrchestratedStageInspectorComponent, ParameterEditorComponent, PasteDialogComponent, ProblemsPanelComponent, RecordFilterEditorComponent, ReferencePickerComponent, ResourcePanelComponent, SEVERITY_BUCKETS, SEVERITY_ICON, SEVERITY_LABEL, START_NODE_NAME, SelectValueDirective, StartInspectorComponent, StructureMemberPickerComponent, StructurePickerComponent, TYPES_WITH_AUTOMATIC_OUTPUT, TYPE_BY_COLLECTION, UNSUPPORTED_TYPES, ValueEditorComponent, VersionPanelComponent, allFieldsOf, applyPaste, areTypesComparable, buildClipboardPayload, canvasNodeId, checkConditionLogic, checkFlowName, describePathEntry, duplicateField, elementIcon, emptyFlowDefinition, fieldAt, filterReferences, flattenFields, flowNodeWidth, flowNodeWidthClass, innerNamesOf, insertField, isClipboardPayload, isCustomConditionLogic, isEmptyReferenceFilter, isFieldResource, isGlobalReference, isNumericType, isPathInside, isTypeCheckedOperator, isValidFlowName, isValued, loadPathLevel, matchesReferenceFilter, moveCondition, moveField, navigatePath, outletByKey, outletsOf, parseCanvasNodeId, parseInvariantNumber, parseSourceConnectorId, parseTargetConnectorId, pathAvailableNames, pathContainerLabel, pathKey, pathNotVerifiableMessage, planPaste, referenceRoot, referencedRootsOf, remapConditionLogic, removeCondition, removeField, resolvePath, rewriteReferences, samePath, screenActionNames, screenFieldNames, severityBucket, slugifyFlowName, sourceConnectorId, stageStepNames, stageStepOutputReferenced, stepsOf, targetConnectorId, typeOfCollection, uniqueFlowName, valuedFieldOf, valuedFieldsOf, variantFieldOf, variantOf, variantPresetOf };
|
|
13875
|
+
export { ConditionEditorComponent, ConnectorEditorComponent, DebugPanelComponent, DynamicScreenInspectorComponent, ElementDialogComponent, ElementInspectorComponent, ElementPaletteComponent, EnumValuePickerComponent, FALLBACK_COLLECTION_BY_TYPE, FALLBACK_TYPE_LABEL, FLOW_BUILDER_HTTP_CONFIG, FLOW_CLIPBOARD_KIND, FLOW_CLIPBOARD_VERSION, FLOW_ELEMENT_ICONS, FLOW_ELEMENT_VARIANT_FIELDS, FLOW_ELEMENT_VARIANT_ICONS, FLOW_ERROR_FALLBACK_MESSAGE, FLOW_ERROR_HTTP_STATUS, FLOW_NAME_PATTERN, FLOW_NODE_COLLECTIONS, FLOW_NODE_HEIGHT, FLOW_NODE_WIDTH, FLOW_REFERENCE_FIELDS, FLOW_RESOURCE_COLLECTIONS, FLOW_VALUE_FIELDS, FLOW_VALUE_LITERAL_FIELDS, FieldAssignmentEditorComponent, FieldPickerComponent, FlowApiError, FlowBuilderApi, FlowBuilderComponent, FlowCanvasComponent, FlowCatalogStore, FlowClipboardService, FlowDictionaryStore, FlowDocumentStore, FlowEditorSession, FlowLayoutService, FlowValidationStore, FormulaEditorComponent, FormulaValidationService, HttpFlowBuilderApi, NamePickerComponent, NodeInspectorBase, ORCHESTRATION_CONDITION_OUTPUT, ObjectPickerComponent, OrchestratedStageInspectorComponent, ParameterEditorComponent, PasteDialogComponent, ProblemsPanelComponent, RecordFilterEditorComponent, ReferencePickerComponent, ResourcePanelComponent, RunDialogComponent, SEVERITY_BUCKETS, SEVERITY_ICON, SEVERITY_LABEL, START_NODE_NAME, SelectValueDirective, StartInspectorComponent, StructureMemberPickerComponent, StructurePickerComponent, TYPES_WITH_AUTOMATIC_OUTPUT, TYPE_BY_COLLECTION, UNSUPPORTED_TYPES, ValueEditorComponent, VersionPanelComponent, allFieldsOf, applyPaste, areTypesComparable, buildClipboardPayload, canvasNodeId, checkConditionLogic, checkFlowName, describePathEntry, duplicateField, elementIcon, emptyFlowDefinition, fieldAt, filterReferences, flattenFields, flowNodeWidth, flowNodeWidthClass, innerNamesOf, insertField, isClipboardPayload, isCustomConditionLogic, isEmptyReferenceFilter, isFieldResource, isGlobalReference, isNumericType, isPathInside, isTypeCheckedOperator, isValidFlowName, isValued, loadPathLevel, matchesReferenceFilter, moveCondition, moveField, navigatePath, outletByKey, outletsOf, parseCanvasNodeId, parseInvariantNumber, parseSourceConnectorId, parseTargetConnectorId, pathAvailableNames, pathContainerLabel, pathKey, pathNotVerifiableMessage, planPaste, referenceRoot, referencedRootsOf, remapConditionLogic, removeCondition, removeField, resolvePath, rewriteReferences, samePath, screenActionNames, screenFieldNames, severityBucket, slugifyFlowName, sourceConnectorId, stageStepNames, stageStepOutputReferenced, stepsOf, targetConnectorId, typeOfCollection, uniqueFlowName, valuedFieldOf, valuedFieldsOf, variantFieldOf, variantOf, variantPresetOf };
|
|
13856
13876
|
//# sourceMappingURL=esfaenza-flow-builder.mjs.map
|