Skip to content

Commit 9d7c454

Browse files
committed
feat: add create project page
1 parent d8c2aa4 commit 9d7c454

7 files changed

Lines changed: 142 additions & 2 deletions

File tree

src/app/app.config.ts

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,11 +4,13 @@ import { provideHttpClient } from '@angular/common/http';
44

55
import { routes } from './app.routes';
66
import { provideAnimations } from '@angular/platform-browser/animations';
7+
import { MessageService } from 'primeng/api';
78

89
export const appConfig: ApplicationConfig = {
910
providers: [
1011
provideZoneChangeDetection({ eventCoalescing: true }),
1112
provideRouter(routes),
1213
provideHttpClient(),
13-
provideAnimations()]
14+
provideAnimations(),
15+
MessageService]
1416
};

src/app/app.routes.ts

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,11 +5,13 @@ import { DashboardComponent } from './features/dashboard/components/dashboard/da
55
import { ProjectKanbanComponent } from './features/projects/components/project-kanban/project-kanban.component';
66
import { TaskItemListComponent } from './features/task-item/components/task-item-list/task-item-list.component';
77
import { UserTaskItemsComponent } from './features/task-item/components/user-task-items/user-task-items.component';
8+
import { ProjectCreateComponent } from './features/projects/components/project-create/project-create.component';
89

910
export const routes: Routes = [
1011
{ path: '', component: DashboardComponent },
1112
{ path: 'projects', component: ProjectListComponent },
1213
{ path: 'projects/kanban', component: ProjectKanbanComponent },
14+
{ path: 'projects/create', component: ProjectCreateComponent },
1315
{ path: 'tasks', component: TaskItemListComponent },
1416
{ path: 'tasks/my-tasks', component: UserTaskItemsComponent },
1517
{ path: 'login', component: HomeLoginComponent },
Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,36 @@
1+
<p-toast />
2+
<p-card header="Create New Project">
3+
<form [formGroup]="projectForm" (ngSubmit)="onSubmit()">
4+
5+
<div class="field mb-4">
6+
<label for="name" class="block text-surface-900 dark:text-surface-0 font-medium mb-2">Project Name</label>
7+
<input id="name" type="text" pInputText formControlName="name" class="w-full" [ngClass]="{'ng-invalid ng-dirty': isInvalid('name')}" />
8+
<small *ngIf="isInvalid('name')" class="p-error block mt-1">
9+
Project name is required.
10+
</small>
11+
</div>
12+
13+
<div class="field mb-4">
14+
<label for="description" class="block text-surface-900 dark:text-surface-0 font-medium mb-2">Description</label>
15+
<textarea id="description" pInputTextarea formControlName="description" class="w-full" [rows]="5"></textarea>
16+
</div>
17+
18+
<div class="flex justify-content-end gap-2">
19+
<p-button
20+
label="Cancel"
21+
icon="pi pi-times"
22+
styleClass="p-button-secondary"
23+
(click)="cancel()"
24+
[disabled]="isLoading">
25+
</p-button>
26+
<p-button
27+
type="submit"
28+
label="Create Project"
29+
icon="pi pi-check"
30+
[loading]="isLoading"
31+
[disabled]="!projectForm.valid || isLoading">
32+
</p-button>
33+
</div>
34+
35+
</form>
36+
</p-card>

src/app/features/projects/components/project-create/project-create.component.scss

Whitespace-only changes.
Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,23 @@
1+
import { ComponentFixture, TestBed } from '@angular/core/testing';
2+
3+
import { ProjectCreateComponent } from './project-create.component';
4+
5+
describe('ProjectCreateComponent', () => {
6+
let component: ProjectCreateComponent;
7+
let fixture: ComponentFixture<ProjectCreateComponent>;
8+
9+
beforeEach(async () => {
10+
await TestBed.configureTestingModule({
11+
imports: [ProjectCreateComponent]
12+
})
13+
.compileComponents();
14+
15+
fixture = TestBed.createComponent(ProjectCreateComponent);
16+
component = fixture.componentInstance;
17+
fixture.detectChanges();
18+
});
19+
20+
it('should create', () => {
21+
expect(component).toBeTruthy();
22+
});
23+
});
Lines changed: 75 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,75 @@
1+
import { Component, OnDestroy, OnInit } from '@angular/core';
2+
import { SharedModule } from '../../../../shared/shared.module';
3+
import { FormBuilder, FormGroup, ReactiveFormsModule, Validators } from '@angular/forms';
4+
import { Subject, takeUntil } from 'rxjs';
5+
import { Router } from '@angular/router';
6+
import { MessageService } from 'primeng/api';
7+
import { ProjectService } from '../../services/project.service';
8+
9+
@Component({
10+
selector: 'app-project-create',
11+
standalone: true,
12+
imports: [SharedModule, ReactiveFormsModule],
13+
templateUrl: './project-create.component.html',
14+
styleUrl: './project-create.component.scss'
15+
})
16+
export class ProjectCreateComponent implements OnInit, OnDestroy {
17+
18+
projectForm!: FormGroup;
19+
isLoading = false;
20+
private destroy$ = new Subject<void>();
21+
22+
constructor(
23+
private fb: FormBuilder,
24+
private projectService: ProjectService,
25+
private router: Router,
26+
private messageService: MessageService
27+
) { }
28+
29+
ngOnInit(): void {
30+
this.projectForm = this.fb.group({
31+
name: ['', Validators.required],
32+
description: ['']
33+
});
34+
}
35+
36+
ngOnDestroy(): void {
37+
this.destroy$.next();
38+
this.destroy$.complete();
39+
}
40+
41+
isInvalid(controlName: string): boolean {
42+
const control = this.projectForm.get(controlName);
43+
return !!control && control.invalid && (control.dirty || control.touched);
44+
}
45+
46+
onSubmit(): void {
47+
if (this.projectForm.invalid) {
48+
this.projectForm.markAllAsTouched();
49+
this.messageService.add({ severity: 'warn', summary: 'Validation Error', detail: 'Please fill in all required fields.' });
50+
return;
51+
}
52+
53+
this.isLoading = true;
54+
const projectData = this.projectForm.value;
55+
56+
this.projectService.createProject(projectData)
57+
.pipe(takeUntil(this.destroy$))
58+
.subscribe({
59+
next: (createdProject) => {
60+
this.isLoading = false;
61+
this.messageService.add({ severity: 'success', summary: 'Success', detail: `Project "${createdProject.name}" created successfully!` });
62+
this.router.navigate(['/projects']);
63+
},
64+
error: (err) => {
65+
this.isLoading = false;
66+
console.error('Error creating project:', err);
67+
this.messageService.add({ severity: 'error', summary: 'Error', detail: 'Could not create project. Please try again.' });
68+
}
69+
});
70+
}
71+
72+
cancel(): void {
73+
this.router.navigate(['/projects']);
74+
}
75+
}

src/app/shared/shared.module.ts

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,7 @@ import { TooltipModule } from 'primeng/tooltip';
2424
import { ProgressSpinnerModule } from 'primeng/progressspinner';
2525
import { RippleModule } from 'primeng/ripple';
2626
import { SkeletonModule } from 'primeng/skeleton';
27+
import { ToastModule } from 'primeng/toast';
2728

2829
@NgModule({
2930
exports: [
@@ -56,7 +57,8 @@ import { SkeletonModule } from 'primeng/skeleton';
5657
TooltipModule,
5758
ProgressSpinnerModule,
5859
RippleModule,
59-
SkeletonModule
60+
SkeletonModule,
61+
ToastModule
6062
],
6163
})
6264
export class SharedModule {}

0 commit comments

Comments
 (0)