diff --git a/src/app/pages/main/editor-sidebar/editor-sidebar.html b/src/app/pages/main/editor-sidebar/editor-sidebar.html index a069cf1..97a5450 100644 --- a/src/app/pages/main/editor-sidebar/editor-sidebar.html +++ b/src/app/pages/main/editor-sidebar/editor-sidebar.html @@ -34,9 +34,15 @@ @switch (open) { @case ('flows') { + @if (!creatingFlow()) { + } @else { + + Creating flow... + + } } diff --git a/src/app/pages/main/editor-sidebar/editor-sidebar.ts b/src/app/pages/main/editor-sidebar/editor-sidebar.ts index 34e12b8..bc37b1e 100644 --- a/src/app/pages/main/editor-sidebar/editor-sidebar.ts +++ b/src/app/pages/main/editor-sidebar/editor-sidebar.ts @@ -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() { diff --git a/src/app/services/flows/flow-mapper.ts b/src/app/services/flows/flow-mapper.ts index bd6aced..88e1379 100644 --- a/src/app/services/flows/flow-mapper.ts +++ b/src/app/services/flows/flow-mapper.ts @@ -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 - }; -} diff --git a/src/app/services/flows/flows-call.base.ts b/src/app/services/flows/flows-call.base.ts index af35ac8..01fcea2 100644 --- a/src/app/services/flows/flows-call.base.ts +++ b/src/app/services/flows/flows-call.base.ts @@ -7,10 +7,10 @@ export abstract class FlowsCallServiceBase { abstract retrieveAllFlows() : Observable; - abstract updateFlow(flow: Flow) : Observable; + abstract updateFlow(flow: Flow) : Observable; abstract createNewFlow(name?: string) : Observable; abstract deleteFlow(flowId: string) : Observable; -} \ No newline at end of file +} diff --git a/src/app/services/flows/flows-call.fake.ts b/src/app/services/flows/flows-call.fake.ts index f01cf0a..ddffa4b 100644 --- a/src/app/services/flows/flows-call.fake.ts +++ b/src/app/services/flows/flows-call.fake.ts @@ -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 { diff --git a/src/app/services/flows/flows-call.ts b/src/app/services/flows/flows-call.ts index 704a313..df91f6f 100644 --- a/src/app/services/flows/flows-call.ts +++ b/src/app/services/flows/flows-call.ts @@ -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 { + override updateFlow(flow: Flow): Observable { const encodedId = encodeURIComponent(flow.id); - return this.http.put(`${environment.apiUrl}/flows/${encodedId}`, toFlowUpdateRequest(flow)); + return this.http + .put( + `${environment.apiUrl}/flows/${encodedId}`, + toFlowCreateRequest(flow.name, flow.description, flow.data) + ) + .pipe(map((raw) => flowFromApi(raw))); } } diff --git a/src/app/services/flows/flows.ts b/src/app/services/flows/flows.ts index 472c4c8..3c169b5 100644 --- a/src/app/services/flows/flows.ts +++ b/src/app/services/flows/flows.ts @@ -52,7 +52,7 @@ export class FlowsService { ); } - cloneFlow(flowId: string): Observable { + cloneFlow(flowId: string): Observable { const originalFlow$ = this.flowsCallService.getFlowById(flowId); const newFlow$ = this.flowsCallService.createNewFlow(); diff --git a/src/app/shared/title-toolbar/title-toolbar.css b/src/app/shared/title-toolbar/title-toolbar.css index e69de29..281738c 100644 --- a/src/app/shared/title-toolbar/title-toolbar.css +++ b/src/app/shared/title-toolbar/title-toolbar.css @@ -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; +} diff --git a/src/app/shared/title-toolbar/title-toolbar.html b/src/app/shared/title-toolbar/title-toolbar.html index e59458f..213d9aa 100644 --- a/src/app/shared/title-toolbar/title-toolbar.html +++ b/src/app/shared/title-toolbar/title-toolbar.html @@ -69,3 +69,9 @@ } } + +@if (snackbarMessage()) { +
+ {{ snackbarMessage() }} +
+} diff --git a/src/app/shared/title-toolbar/title-toolbar.ts b/src/app/shared/title-toolbar/title-toolbar.ts index 3a351d3..fea324a 100644 --- a/src/app/shared/title-toolbar/title-toolbar.ts +++ b/src/app/shared/title-toolbar/title-toolbar.ts @@ -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 | null = null; @ViewChild('titleInput') myInputRef!: ElementRef; @@ -20,6 +22,8 @@ export class TitleToolbar { }); notSaved = computed(() => this.editorState.isDirty()); + snackbarMessage = signal(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); + } }