From 262492b628b00de5a4f3003f0afce12a31184d89 Mon Sep 17 00:00:00 2001 From: Yash Rastogi Date: Sat, 11 Apr 2026 17:44:17 -0400 Subject: [PATCH 1/4] fix(workspace): resolve indefinite loading state on edit page (ref Sentinent-AI/Sentinent#24) --- .../components/workspace/edit-workspace.html | 2 +- .../components/workspace/edit-workspace.ts | 38 ++++++++++++++----- 2 files changed, 30 insertions(+), 10 deletions(-) diff --git a/src/app/components/workspace/edit-workspace.html b/src/app/components/workspace/edit-workspace.html index fc328b8..dea5561 100644 --- a/src/app/components/workspace/edit-workspace.html +++ b/src/app/components/workspace/edit-workspace.html @@ -24,7 +24,7 @@

Edit Workspace

Loading workspace... -
+
{ @@ -35,13 +37,24 @@ export class EditWorkspaceComponent implements OnInit { } loadWorkspace(id: string): void { - this.workspaceService.getWorkspace(id).subscribe(workspace => { - this.isLoading = false; - if (workspace) { - this.name = workspace.name; - this.description = workspace.description || ''; - } else { - this.error = 'Workspace not found'; + this.workspaceService.getWorkspace(id).subscribe({ + next: workspace => { + this.isLoading = false; + if (workspace) { + this.hasWorkspace = true; + this.name = workspace.name; + this.description = workspace.description || ''; + } else { + this.hasWorkspace = false; + this.error = 'Workspace not found'; + } + this.cdr.detectChanges(); + }, + error: () => { + this.isLoading = false; + this.hasWorkspace = false; + this.error = 'Unable to load workspace. Please try again.'; + this.cdr.detectChanges(); } }); } @@ -57,7 +70,14 @@ export class EditWorkspaceComponent implements OnInit { this.error = ''; this.workspaceService.updateWorkspace(this.workspaceId, trimmedName, this.description.trim()).subscribe({ - next: () => this.router.navigate(['/dashboard']), + next: (workspace) => { + if (workspace) { + this.router.navigate(['/dashboard']); + } else { + this.error = 'Workspace not found.'; + this.isSubmitting = false; + } + }, error: () => { this.error = 'Unable to update workspace. Please try again.'; this.isSubmitting = false; From 7a818859710cece3983b1e58678ffbe14e951253 Mon Sep 17 00:00:00 2001 From: Yash Rastogi Date: Sat, 11 Apr 2026 17:57:20 -0400 Subject: [PATCH 2/4] fix(dashboard): update UI after workspace deletion and signal load (ref Sentinent-AI/Sentinent#25) --- src/app/components/dashboard/dashboard.ts | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/src/app/components/dashboard/dashboard.ts b/src/app/components/dashboard/dashboard.ts index 349ae97..7909b63 100644 --- a/src/app/components/dashboard/dashboard.ts +++ b/src/app/components/dashboard/dashboard.ts @@ -29,8 +29,11 @@ export class Dashboard implements OnInit { filters: SignalFilters = { source: 'all', status: 'all' }; githubBanner = ''; slackBanner = ''; + currentUserId: string | null = null; ngOnInit() { + this.currentUserId = this.authService.getCurrentUserId(); + this.workspaceService.getWorkspaces().subscribe({ next: ws => { this.workspaces = ws; @@ -57,6 +60,10 @@ export class Dashboard implements OnInit { this.loadSignals(); } + canEditWorkspace(workspace: Workspace): boolean { + return this.currentUserId !== null && workspace.ownerId === this.currentUserId; + } + deleteWorkspace(workspace: Workspace) { const confirmed = window.confirm(`Delete workspace "${workspace.name}"?`); if (!confirmed) { @@ -68,6 +75,7 @@ export class Dashboard implements OnInit { return; } this.workspaces = this.workspaces.filter(ws => ws.id !== workspace.id); + this.cdr.detectChanges(); }); } @@ -97,6 +105,7 @@ export class Dashboard implements OnInit { private loadSignals(): void { this.signalService.getSignals(this.filters).subscribe(signals => { this.signals = signals; + this.cdr.detectChanges(); }); } } From 6121919ae23a815f99c8330b427590feaf66e946 Mon Sep 17 00:00:00 2001 From: Yash Rastogi Date: Sat, 11 Apr 2026 18:01:34 -0400 Subject: [PATCH 3/4] fix(dashboard): add missing getCurrentUserId to AuthService --- src/app/services/auth.ts | 20 ++++++++++++++++++++ 1 file changed, 20 insertions(+) diff --git a/src/app/services/auth.ts b/src/app/services/auth.ts index 2f58e01..846a1af 100644 --- a/src/app/services/auth.ts +++ b/src/app/services/auth.ts @@ -37,6 +37,26 @@ export class AuthService { return !!this.getToken(); } + getCurrentUserId(): string | null { + const token = this.getToken(); + if (!token) { + return null; + } + + const payload = token.split('.')[1]; + if (!payload) { + return null; + } + + try { + const normalizedPayload = payload.replace(/-/g, '+').replace(/_/g, '/'); + const decodedPayload = JSON.parse(atob(normalizedPayload)) as { user_id?: number | string }; + return decodedPayload.user_id === undefined ? null : String(decodedPayload.user_id); + } catch { + return null; + } + } + getToken(): string | null { return localStorage.getItem(this.tokenKey); } From b57a944ea31305265b3b0495882589bcaf505767 Mon Sep 17 00:00:00 2001 From: Yash Rastogi Date: Sat, 11 Apr 2026 18:01:50 -0400 Subject: [PATCH 4/4] fix(dashboard): restrict edit access to owners and update tests --- src/app/components/dashboard/dashboard.html | 2 +- .../components/dashboard/dashboard.spec.ts | 21 ++++++++++++++++++- 2 files changed, 21 insertions(+), 2 deletions(-) diff --git a/src/app/components/dashboard/dashboard.html b/src/app/components/dashboard/dashboard.html index f39c299..515456e 100644 --- a/src/app/components/dashboard/dashboard.html +++ b/src/app/components/dashboard/dashboard.html @@ -37,7 +37,7 @@

{{ ws.name }}

Created: {{ ws.createdDate | date: 'mediumDate' }}
Open - Edit + Edit
diff --git a/src/app/components/dashboard/dashboard.spec.ts b/src/app/components/dashboard/dashboard.spec.ts index c5f36f4..e2b3827 100644 --- a/src/app/components/dashboard/dashboard.spec.ts +++ b/src/app/components/dashboard/dashboard.spec.ts @@ -23,7 +23,8 @@ describe('Dashboard', () => { }; mockAuthService = { - logout: jasmine.createSpy('logout') + logout: jasmine.createSpy('logout'), + getCurrentUserId: jasmine.createSpy('getCurrentUserId').and.returnValue('u1') }; mockSignalService = { @@ -91,6 +92,24 @@ describe('Dashboard', () => { expect(compiled.querySelector('.workspace-card h3')?.textContent).toContain('Test Workspace'); }); + it('should hide edit action for workspaces not owned by the current user', () => { + mockWorkspaceService.getWorkspaces.and.returnValue(of([ + { id: '1', name: 'Owned Workspace', description: 'Owned Desc', createdDate: new Date(), ownerId: 'u1' }, + { id: '2', name: 'Shared Workspace', description: 'Shared Desc', createdDate: new Date(), ownerId: 'u2' } + ])); + + fixture = TestBed.createComponent(Dashboard); + component = fixture.componentInstance; + fixture.detectChanges(); + + const cards = Array.from(fixture.nativeElement.querySelectorAll('.workspace-card')) as HTMLElement[]; + const ownedCard = cards.find(card => card.textContent?.includes('Owned Workspace')); + const sharedCard = cards.find(card => card.textContent?.includes('Shared Workspace')); + + expect(ownedCard?.textContent).toContain('Edit'); + expect(sharedCard?.textContent).not.toContain('Edit'); + }); + it('should call logout on button click', () => { const button = fixture.nativeElement.querySelector('.logout-btn'); button.click();