Align flow save API integration and add editor save/create feedback
This commit is contained in:
parent
a2f7152202
commit
e13e06d858
|
|
@ -34,9 +34,15 @@
|
|||
@switch (open) {
|
||||
@case ('flows') {
|
||||
<app-group-holder title="Flows" icon="bi-lightning-charge-fill">
|
||||
@if (!creatingFlow()) {
|
||||
<i ngProjectAs="header-button"
|
||||
class="bi bi-plus-circle-fill text-blue-600 hover:cursor-pointer hover:text-blue-400 text-lg"
|
||||
(click)="createNewFlow()"></i>
|
||||
} @else {
|
||||
<span ngProjectAs="header-button" class="spinner-border spinner-border-sm text-blue-600" role="status">
|
||||
<span class="visually-hidden">Creating flow...</span>
|
||||
</span>
|
||||
}
|
||||
<app-flows-list></app-flows-list>
|
||||
</app-group-holder>
|
||||
}
|
||||
|
|
|
|||
|
|
@ -6,6 +6,7 @@ import { EditorStateHolder } from '@stores/flow-editor';
|
|||
import { CommonModule } from '@angular/common';
|
||||
import { FlowsService } from '@services/flows/flows';
|
||||
import { ListState } from '@stores/list-state';
|
||||
import { finalize } from 'rxjs';
|
||||
|
||||
type OpenedId = 'flows' | 'blocks';
|
||||
|
||||
|
|
@ -25,6 +26,7 @@ export class EditorSidebar {
|
|||
blockDisabled = computed(() => !this.flowState.hasFlow());
|
||||
|
||||
collapsed = signal(true);
|
||||
creatingFlow = signal(false);
|
||||
|
||||
open: OpenedId | null = null;
|
||||
|
||||
|
|
@ -40,16 +42,20 @@ export class EditorSidebar {
|
|||
}
|
||||
|
||||
createNewFlow() {
|
||||
if (this.creatingFlow()) return;
|
||||
this.creatingFlow.set(true);
|
||||
console.log('Creating new flow...');
|
||||
this.flowService.createNewFlow().subscribe({
|
||||
next: flow => {
|
||||
console.log('New flow created:', flow);
|
||||
this.flowState.openDocument(flow);
|
||||
},
|
||||
error: err => {
|
||||
console.error('Error creating new flow:', err);
|
||||
},
|
||||
});
|
||||
this.flowService.createNewFlow().pipe(
|
||||
finalize(() => this.creatingFlow.set(false))
|
||||
).subscribe({
|
||||
next: flow => {
|
||||
console.log('New flow created:', flow);
|
||||
this.flowState.openDocument(flow);
|
||||
},
|
||||
error: err => {
|
||||
console.error('Error creating new flow:', err);
|
||||
},
|
||||
});
|
||||
}
|
||||
|
||||
createNewBlock() {
|
||||
|
|
|
|||
|
|
@ -44,13 +44,3 @@ export function toFlowCreateRequest(name: string, description?: string, flow?: F
|
|||
}
|
||||
};
|
||||
}
|
||||
|
||||
export function toFlowUpdateRequest(flow: Flow) {
|
||||
return {
|
||||
name: flow.name,
|
||||
description: flow.description,
|
||||
published: flow.published ?? flow.visibility === 'PUBLIC',
|
||||
finalized: flow.finalized ?? false,
|
||||
flow: flow.data
|
||||
};
|
||||
}
|
||||
|
|
|
|||
|
|
@ -7,10 +7,10 @@ export abstract class FlowsCallServiceBase {
|
|||
|
||||
abstract retrieveAllFlows() : Observable<Flow[]>;
|
||||
|
||||
abstract updateFlow(flow: Flow) : Observable<void>;
|
||||
abstract updateFlow(flow: Flow) : Observable<Flow>;
|
||||
|
||||
abstract createNewFlow(name?: string) : Observable<Flow>;
|
||||
|
||||
abstract deleteFlow(flowId: string) : Observable<void>;
|
||||
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -28,7 +28,7 @@ export class FlowsCallServiceFake extends FlowsCallServiceBase {
|
|||
|
||||
override updateFlow(flow: Flow) {
|
||||
this.data[flow.id] = flow;
|
||||
return of(void 0);
|
||||
return of(flow);
|
||||
}
|
||||
|
||||
override createNewFlow(name?: string): Observable<Flow> {
|
||||
|
|
|
|||
|
|
@ -3,7 +3,7 @@ import { inject } from '@angular/core';
|
|||
import { environment } from '@environment';
|
||||
import { Flow } from '@models/flow';
|
||||
import { map, Observable } from 'rxjs';
|
||||
import { flowFromApi, toFlowCreateRequest, toFlowUpdateRequest } from './flow-mapper';
|
||||
import { flowFromApi, toFlowCreateRequest } from './flow-mapper';
|
||||
import { FlowsCallServiceBase } from './flows-call.base';
|
||||
|
||||
export class FlowsCallService extends FlowsCallServiceBase {
|
||||
|
|
@ -33,8 +33,13 @@ export class FlowsCallService extends FlowsCallServiceBase {
|
|||
.pipe(map((raw) => raw.map((flow) => flowFromApi(flow))));
|
||||
}
|
||||
|
||||
override updateFlow(flow: Flow): Observable<void> {
|
||||
override updateFlow(flow: Flow): Observable<Flow> {
|
||||
const encodedId = encodeURIComponent(flow.id);
|
||||
return this.http.put<void>(`${environment.apiUrl}/flows/${encodedId}`, toFlowUpdateRequest(flow));
|
||||
return this.http
|
||||
.put<unknown>(
|
||||
`${environment.apiUrl}/flows/${encodedId}`,
|
||||
toFlowCreateRequest(flow.name, flow.description, flow.data)
|
||||
)
|
||||
.pipe(map((raw) => flowFromApi(raw)));
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -52,7 +52,7 @@ export class FlowsService {
|
|||
);
|
||||
}
|
||||
|
||||
cloneFlow(flowId: string): Observable<void> {
|
||||
cloneFlow(flowId: string): Observable<Flow> {
|
||||
const originalFlow$ = this.flowsCallService.getFlowById(flowId);
|
||||
|
||||
const newFlow$ = this.flowsCallService.createNewFlow();
|
||||
|
|
|
|||
|
|
@ -0,0 +1,17 @@
|
|||
.save-snackbar {
|
||||
position: fixed;
|
||||
right: 24px;
|
||||
bottom: 24px;
|
||||
z-index: 2000;
|
||||
padding: 10px 14px;
|
||||
border-radius: 8px;
|
||||
background: #15803d;
|
||||
color: #ffffff;
|
||||
font-size: 13px;
|
||||
font-weight: 500;
|
||||
box-shadow: 0 10px 20px rgba(15, 23, 42, 0.22);
|
||||
}
|
||||
|
||||
.save-snackbar-error {
|
||||
background: #b91c1c;
|
||||
}
|
||||
|
|
@ -69,3 +69,9 @@
|
|||
}
|
||||
</div>
|
||||
}
|
||||
|
||||
@if (snackbarMessage()) {
|
||||
<div class="save-snackbar" [class.save-snackbar-error]="snackbarType() === 'error'">
|
||||
{{ snackbarMessage() }}
|
||||
</div>
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,5 +1,6 @@
|
|||
import { CommonModule } from '@angular/common';
|
||||
import { Component, computed, ElementRef, inject, ViewChild } from '@angular/core';
|
||||
import { Component, computed, ElementRef, inject, signal, ViewChild } from '@angular/core';
|
||||
import { take } from 'rxjs';
|
||||
import { EditorStateHolder } from '@stores/flow-editor';
|
||||
|
||||
@Component({
|
||||
|
|
@ -9,6 +10,7 @@ import { EditorStateHolder } from '@stores/flow-editor';
|
|||
styleUrl: './title-toolbar.css',
|
||||
})
|
||||
export class TitleToolbar {
|
||||
private snackTimeout: ReturnType<typeof setTimeout> | null = null;
|
||||
|
||||
@ViewChild('titleInput') myInputRef!: ElementRef;
|
||||
|
||||
|
|
@ -20,6 +22,8 @@ export class TitleToolbar {
|
|||
});
|
||||
|
||||
notSaved = computed(() => this.editorState.isDirty());
|
||||
snackbarMessage = signal<string | null>(null);
|
||||
snackbarType = signal<'success' | 'error'>('success');
|
||||
|
||||
|
||||
changeTitle(value: string) {
|
||||
|
|
@ -35,9 +39,18 @@ export class TitleToolbar {
|
|||
|
||||
save() {
|
||||
if (!this.notSaved()) return;
|
||||
this.editorState.save().subscribe(
|
||||
err => console.error('Save failed', err)
|
||||
);
|
||||
this.editorState.save().pipe(
|
||||
take(1)
|
||||
).subscribe({
|
||||
next: () => {
|
||||
console.log('Flow saved');
|
||||
this.showSnackbar('Flow saved', 'success');
|
||||
},
|
||||
error: err => {
|
||||
console.error('Save failed', err);
|
||||
this.showSnackbar('Errore durante il salvataggio', 'error');
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
undo() {
|
||||
|
|
@ -46,5 +59,19 @@ export class TitleToolbar {
|
|||
|
||||
redo() {
|
||||
this.editorState.redo();
|
||||
}
|
||||
}
|
||||
|
||||
private showSnackbar(message: string, type: 'success' | 'error') {
|
||||
this.snackbarMessage.set(message);
|
||||
this.snackbarType.set(type);
|
||||
|
||||
if (this.snackTimeout) {
|
||||
clearTimeout(this.snackTimeout);
|
||||
}
|
||||
|
||||
this.snackTimeout = setTimeout(() => {
|
||||
this.snackbarMessage.set(null);
|
||||
this.snackTimeout = null;
|
||||
}, 2500);
|
||||
}
|
||||
}
|
||||
|
|
|
|||
Loading…
Reference in New Issue