Skip to content

Commit 857aa70

Browse files
authored
fix(workspace): support decisions in newly created workspaces (#12)
* feat(workspace): enhance dashboard actions and workspace details (ref Sentinent-AI/Sentinent#4) * fix(workspace): enable decision creation for new workspaces (ref Sentinent-AI/Sentinent#4)
1 parent 1268b5b commit 857aa70

10 files changed

Lines changed: 157 additions & 44 deletions

File tree

src/app/components/dashboard/dashboard.css

Lines changed: 41 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -55,17 +55,6 @@
5555
place-items: center;
5656
}
5757

58-
nav {
59-
display: flex;
60-
align-items: center;
61-
gap: 20px;
62-
}
63-
64-
.workspace-card-link {
65-
text-decoration: none;
66-
color: inherit;
67-
}
68-
6958
.logout-btn {
7059
border: 1px solid rgba(255, 255, 255, 0.24);
7160
background: transparent;
@@ -158,6 +147,47 @@ nav {
158147
letter-spacing: 0.04em;
159148
}
160149

150+
.card-actions {
151+
margin-top: 14px;
152+
display: flex;
153+
gap: 8px;
154+
}
155+
156+
.action-btn {
157+
text-decoration: none;
158+
display: inline-flex;
159+
align-items: center;
160+
border-radius: 8px;
161+
border: 1px solid #d0d0d0;
162+
padding: 7px 12px;
163+
font-size: 13px;
164+
cursor: pointer;
165+
background: #fff;
166+
color: #111;
167+
}
168+
169+
.open-btn {
170+
background: #f3f3f3;
171+
}
172+
173+
.open-btn:hover {
174+
background: #e9e9e9;
175+
}
176+
177+
.edit-btn:hover {
178+
background: #f3f3f3;
179+
}
180+
181+
.delete-btn {
182+
border-color: #2b2b2b;
183+
background: #111;
184+
color: #fff;
185+
}
186+
187+
.delete-btn:hover {
188+
background: #000;
189+
}
190+
161191
.empty-state {
162192
border: 1px dashed #bcbcbc;
163193
border-radius: 12px;

src/app/components/dashboard/dashboard.html

Lines changed: 10 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -27,18 +27,17 @@ <h2>Your Workspaces</h2>
2727
<a routerLink="/workspace/create" class="create-btn">Create Workspace</a>
2828
</div>
2929

30-
<div class="workspace-list">
31-
<a *ngFor="let ws of workspaces" [routerLink]="['/workspaces', ws.id]" class="workspace-card-link">
32-
<div class="workspace-card">
33-
<h3>{{ ws.name }}</h3>
34-
<p>{{ ws.description }}</p>
35-
<small>Created: {{ ws.createdDate | date }}</small>
30+
<div class="workspace-list" *ngIf="workspaces.length > 0; else empty">
31+
<article *ngFor="let ws of workspaces" class="workspace-card">
32+
<h3>{{ ws.name }}</h3>
33+
<p>{{ ws.description || 'No description yet.' }}</p>
34+
<small>Created: {{ ws.createdDate | date: 'mediumDate' }}</small>
35+
<div class="card-actions">
36+
<a [routerLink]="['/workspaces', ws.id, 'decisions']" class="action-btn open-btn">Open</a>
37+
<button type="button" class="action-btn edit-btn" (click)="editWorkspace(ws)">Edit</button>
38+
<button type="button" class="action-btn delete-btn" (click)="deleteWorkspace(ws)">Delete</button>
3639
</div>
37-
</a>
38-
</div>
39-
40-
<div *ngIf="workspaces.length === 0" class="empty-state">
41-
<p>No workspaces found. Create one to get started!</p>
40+
</article>
4241
</div>
4342

4443
<ng-template #empty>

src/app/components/dashboard/dashboard.ts

Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,41 @@ export class Dashboard implements OnInit {
2525
});
2626
}
2727

28+
editWorkspace(workspace: Workspace) {
29+
const updatedName = window.prompt('Edit workspace name', workspace.name)?.trim();
30+
if (!updatedName) {
31+
return;
32+
}
33+
34+
const updatedDescriptionInput = window.prompt('Edit workspace description', workspace.description ?? '');
35+
if (updatedDescriptionInput === null) {
36+
return;
37+
}
38+
39+
const updatedDescription = updatedDescriptionInput.trim();
40+
41+
this.workspaceService.updateWorkspace(workspace.id, updatedName, updatedDescription).subscribe(updatedWorkspace => {
42+
if (!updatedWorkspace) {
43+
return;
44+
}
45+
this.workspaces = this.workspaces.map(ws => ws.id === updatedWorkspace.id ? updatedWorkspace : ws);
46+
});
47+
}
48+
49+
deleteWorkspace(workspace: Workspace) {
50+
const confirmed = window.confirm(`Delete workspace "${workspace.name}"?`);
51+
if (!confirmed) {
52+
return;
53+
}
54+
55+
this.workspaceService.deleteWorkspace(workspace.id).subscribe(deleted => {
56+
if (!deleted) {
57+
return;
58+
}
59+
this.workspaces = this.workspaces.filter(ws => ws.id !== workspace.id);
60+
});
61+
}
62+
2863
logout() {
2964
this.authService.logout();
3065
this.router.navigate(['/login']);

src/app/components/decision-form/decision-form.component.ts

Lines changed: 17 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,7 @@ export class DecisionFormComponent implements OnInit {
1717
isEditMode = false;
1818
decisionId: string | null = null;
1919
isLoading = false;
20+
workspaceId: string | null = null;
2021

2122
constructor(
2223
private fb: FormBuilder,
@@ -32,13 +33,7 @@ export class DecisionFormComponent implements OnInit {
3233
}
3334

3435
ngOnInit(): void {
35-
// Get workspaceId from parent
36-
this.route.parent?.paramMap.subscribe(params => {
37-
const workspaceId = params.get('id');
38-
if (workspaceId) {
39-
this.decisionForm.patchValue({ workspaceId });
40-
}
41-
});
36+
this.workspaceId = this.getWorkspaceIdFromRoute();
4237

4338
// Get decisionId from current route
4439
this.route.paramMap.subscribe(params => {
@@ -78,11 +73,23 @@ export class DecisionFormComponent implements OnInit {
7873
this.router.navigate(['../../'], { relativeTo: this.route });
7974
});
8075
} else {
81-
// Include workspaceId from parent route if available
82-
const workspaceId = this.route.parent?.snapshot.paramMap.get('id');
83-
this.decisionService.createDecision({ ...formValue, workspaceId }).subscribe(() => {
76+
if (!this.workspaceId) {
77+
return;
78+
}
79+
80+
this.decisionService.createDecision({ ...formValue, workspaceId: this.workspaceId }).subscribe(() => {
8481
this.router.navigate(['../'], { relativeTo: this.route });
8582
});
8683
}
8784
}
85+
86+
private getWorkspaceIdFromRoute(): string | null {
87+
for (const route of this.route.pathFromRoot) {
88+
const id = route.snapshot.paramMap.get('id');
89+
if (id) {
90+
return id;
91+
}
92+
}
93+
return null;
94+
}
8895
}

src/app/components/decision-list/decision-list.component.ts

Lines changed: 14 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -21,18 +21,25 @@ export class DecisionListComponent implements OnInit {
2121
) { }
2222

2323
ngOnInit(): void {
24-
// Get workspaceId from the parent route (workspaces/:id)
25-
this.route.parent?.paramMap.subscribe(params => {
26-
const workspaceId = params.get('id');
27-
if (workspaceId) {
28-
this.decisions$ = this.decisionService.getDecisions(workspaceId);
29-
}
30-
});
24+
const workspaceId = this.getWorkspaceIdFromRoute();
25+
if (workspaceId) {
26+
this.decisions$ = this.decisionService.getDecisions(workspaceId);
27+
}
3128
}
3229

3330
deleteDecision(id: string): void {
3431
if (confirm('Are you sure you want to delete this decision?')) {
3532
this.decisionService.deleteDecision(id);
3633
}
3734
}
35+
36+
private getWorkspaceIdFromRoute(): string | null {
37+
for (const route of this.route.pathFromRoot) {
38+
const id = route.snapshot.paramMap.get('id');
39+
if (id) {
40+
return id;
41+
}
42+
}
43+
return null;
44+
}
3845
}

src/app/components/workspace/create-workspace.css

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -28,10 +28,13 @@ label {
2828
font-weight: 600;
2929
}
3030

31-
input {
31+
input,
32+
textarea {
3233
padding: 10px 12px;
3334
border: 1px solid #d0d7de;
3435
border-radius: 6px;
36+
font: inherit;
37+
resize: vertical;
3538
}
3639

3740
.actions {

src/app/components/workspace/create-workspace.html

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,16 @@ <h2>Create Workspace</h2>
1414
placeholder="e.g. Product Team"
1515
/>
1616

17+
<label for="workspace-description">Description</label>
18+
<textarea
19+
id="workspace-description"
20+
name="workspaceDescription"
21+
[(ngModel)]="description"
22+
[disabled]="isSubmitting"
23+
rows="4"
24+
placeholder="Describe this workspace"
25+
></textarea>
26+
1727
<div class="actions">
1828
<button type="submit" [disabled]="isSubmitting || !workspaceForm.form.valid">
1929
{{ isSubmitting ? 'Creating...' : 'Create Workspace' }}

src/app/components/workspace/create-workspace.ts

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@ import { WorkspaceService } from '../../services/workspace';
1313
})
1414
export class CreateWorkspace {
1515
name = '';
16+
description = '';
1617
error = '';
1718
isSubmitting = false;
1819

@@ -29,7 +30,7 @@ export class CreateWorkspace {
2930
this.isSubmitting = true;
3031
this.error = '';
3132

32-
this.workspaceService.createWorkspace(trimmedName).subscribe({
33+
this.workspaceService.createWorkspace(trimmedName, this.description.trim()).subscribe({
3334
next: () => this.router.navigate(['/dashboard']),
3435
error: () => {
3536
this.error = 'Unable to create workspace. Please try again.';

src/app/services/decision.service.ts

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -52,12 +52,16 @@ export class DecisionService {
5252
}
5353

5454
createDecision(decision: Partial<Decision>): Observable<Decision> {
55+
if (!decision.workspaceId) {
56+
throw new Error('workspaceId is required to create a decision');
57+
}
58+
5559
const newDecision: Decision = {
5660
id: Math.random().toString(36).substring(2, 9),
5761
title: decision.title!,
5862
description: decision.description,
5963
status: decision.status || 'DRAFT',
60-
workspaceId: decision.workspaceId || 'ws-1', // Default to ws-1 for now
64+
workspaceId: decision.workspaceId,
6165
userId: 'user-1', // Mock user
6266
createdAt: new Date(),
6367
updatedAt: new Date(),

src/app/services/workspace.ts

Lines changed: 19 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -38,15 +38,32 @@ export class WorkspaceService {
3838
return of(this.mockWorkspaces.find(w => w.id === id));
3939
}
4040

41-
createWorkspace(name: string): Observable<Workspace> {
41+
createWorkspace(name: string, description: string): Observable<Workspace> {
4242
const newWorkspace: Workspace = {
4343
id: Math.random().toString(36).substring(7),
4444
name,
45-
description: '',
45+
description,
4646
createdDate: new Date(),
4747
ownerId: 'user1' // Mock owner
4848
};
4949
this.mockWorkspaces.push(newWorkspace);
5050
return of(newWorkspace);
5151
}
52+
53+
updateWorkspace(id: string, name: string, description: string): Observable<Workspace | undefined> {
54+
const workspace = this.mockWorkspaces.find(w => w.id === id);
55+
if (!workspace) {
56+
return of(undefined);
57+
}
58+
59+
workspace.name = name;
60+
workspace.description = description;
61+
return of(workspace);
62+
}
63+
64+
deleteWorkspace(id: string): Observable<boolean> {
65+
const initialLength = this.mockWorkspaces.length;
66+
this.mockWorkspaces = this.mockWorkspaces.filter(w => w.id !== id);
67+
return of(this.mockWorkspaces.length < initialLength);
68+
}
5269
}

0 commit comments

Comments
 (0)