mirror of
https://github.com/a-mayb3/KanbanCloneAngular.git
synced 2026-03-21 18:05:38 +01:00
Created task creation page
This commit is contained in:
parent
782f58a4a2
commit
dd043b3385
5 changed files with 254 additions and 13 deletions
91
src/app/pages/task-create/task-create.component.css
Normal file
91
src/app/pages/task-create/task-create.component.css
Normal file
|
|
@ -0,0 +1,91 @@
|
|||
.create-task {
|
||||
min-height: 100vh;
|
||||
background-color: #f5f7fa;
|
||||
padding: 40px;
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
align-items: flex-start;
|
||||
}
|
||||
|
||||
.card {
|
||||
width: 100%;
|
||||
max-width: 640px;
|
||||
height: fit-content;
|
||||
background: white;
|
||||
border-radius: 12px;
|
||||
padding: 32px;
|
||||
box-shadow: 0 2px 8px rgba(0, 0, 0, 0.1);
|
||||
}
|
||||
|
||||
.card h1 {
|
||||
margin: 0 0 24px 0;
|
||||
font-size: 28px;
|
||||
color: #2c3e50;
|
||||
}
|
||||
|
||||
.form-group {
|
||||
margin-bottom: 20px;
|
||||
}
|
||||
|
||||
.form-group label {
|
||||
display: block;
|
||||
margin-bottom: 8px;
|
||||
font-weight: 600;
|
||||
color: #374151;
|
||||
}
|
||||
|
||||
.form-group input,
|
||||
.form-group textarea,
|
||||
.form-group select {
|
||||
width: 100%;
|
||||
padding: 12px 14px;
|
||||
border: 1px solid #e5e7eb;
|
||||
border-radius: 8px;
|
||||
font-size: 14px;
|
||||
box-sizing: border-box;
|
||||
background-color: #ffffff;
|
||||
}
|
||||
|
||||
.form-group textarea {
|
||||
resize: vertical;
|
||||
}
|
||||
|
||||
.actions {
|
||||
display: flex;
|
||||
justify-content: flex-end;
|
||||
gap: 12px;
|
||||
margin-top: 12px;
|
||||
}
|
||||
|
||||
.btn-primary {
|
||||
padding: 10px 16px;
|
||||
background-color: #10b981;
|
||||
color: #fff;
|
||||
border: none;
|
||||
border-radius: 8px;
|
||||
font-weight: 600;
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
.btn-primary:disabled {
|
||||
opacity: 0.6;
|
||||
cursor: not-allowed;
|
||||
}
|
||||
|
||||
.btn-secondary {
|
||||
padding: 10px 16px;
|
||||
background: none;
|
||||
border: 1px solid #cbd5f0;
|
||||
color: #334155;
|
||||
border-radius: 8px;
|
||||
font-weight: 600;
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
.error {
|
||||
margin-bottom: 16px;
|
||||
padding: 12px;
|
||||
border-radius: 8px;
|
||||
background-color: #fee2e2;
|
||||
color: #b91c1c;
|
||||
}
|
||||
56
src/app/pages/task-create/task-create.component.html
Normal file
56
src/app/pages/task-create/task-create.component.html
Normal file
|
|
@ -0,0 +1,56 @@
|
|||
<div class="create-task">
|
||||
<div class="card">
|
||||
<h1>Create task</h1>
|
||||
|
||||
@if (errorMessage()) {
|
||||
<div class="error">{{ errorMessage() }}</div>
|
||||
}
|
||||
|
||||
<form (ngSubmit)="onSubmit()" #taskForm="ngForm">
|
||||
<div class="form-group">
|
||||
<label for="title">Task title</label>
|
||||
<input
|
||||
id="title"
|
||||
type="text"
|
||||
name="title"
|
||||
[(ngModel)]="title"
|
||||
placeholder="Enter a task title"
|
||||
required
|
||||
/>
|
||||
</div>
|
||||
|
||||
<div class="form-group">
|
||||
<label for="description">Description</label>
|
||||
<textarea
|
||||
id="description"
|
||||
name="description"
|
||||
[(ngModel)]="description"
|
||||
rows="4"
|
||||
placeholder="Enter a task description"
|
||||
></textarea>
|
||||
</div>
|
||||
|
||||
<div class="form-group">
|
||||
<label for="status">Status</label>
|
||||
<select id="status" name="status" [(ngModel)]="status" required>
|
||||
<option value="pending">Pending</option>
|
||||
<option value="in_progress">In progress</option>
|
||||
<option value="completed">Completed</option>
|
||||
<option value="stashed">Stashed</option>
|
||||
<option value="failed">Failed</option>
|
||||
</select>
|
||||
</div>
|
||||
|
||||
<div class="actions">
|
||||
<button type="button" class="btn-secondary" (click)="onCancel()">Cancel</button>
|
||||
<button type="submit" class="btn-primary" [disabled]="isSaving() || !taskForm.form.valid">
|
||||
@if (isSaving()) {
|
||||
<span>Creating...</span>
|
||||
} @else {
|
||||
<span>Create task</span>
|
||||
}
|
||||
</button>
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
78
src/app/pages/task-create/task-create.component.ts
Normal file
78
src/app/pages/task-create/task-create.component.ts
Normal file
|
|
@ -0,0 +1,78 @@
|
|||
import { Component, inject, signal } from '@angular/core';
|
||||
import { CommonModule } from '@angular/common';
|
||||
import { FormsModule } from '@angular/forms';
|
||||
import { ActivatedRoute, Router } from '@angular/router';
|
||||
import { ApiService } from '../../services/api.service';
|
||||
import { CreateTaskRequest, Task } from '../../models/tasks.models';
|
||||
|
||||
@Component({
|
||||
selector: 'app-task-create',
|
||||
standalone: true,
|
||||
imports: [CommonModule, FormsModule],
|
||||
templateUrl: './task-create.component.html',
|
||||
styleUrls: ['./task-create.component.css'],
|
||||
})
|
||||
export class TaskCreateComponent {
|
||||
private apiService = inject(ApiService);
|
||||
private route = inject(ActivatedRoute);
|
||||
private router = inject(Router);
|
||||
|
||||
title = '';
|
||||
description = '';
|
||||
status: Task['status'] = 'pending';
|
||||
isSaving = signal(false);
|
||||
errorMessage = signal('');
|
||||
private projectId: number | null = null;
|
||||
|
||||
constructor() {
|
||||
const idParam = this.route.snapshot.paramMap.get('id');
|
||||
const projectId = idParam ? Number(idParam) : Number.NaN;
|
||||
|
||||
if (!Number.isFinite(projectId)) {
|
||||
this.errorMessage.set('Invalid project id.');
|
||||
return;
|
||||
}
|
||||
|
||||
this.projectId = projectId;
|
||||
}
|
||||
|
||||
onSubmit() {
|
||||
if (!this.title.trim()) {
|
||||
this.errorMessage.set('Task title is required.');
|
||||
return;
|
||||
}
|
||||
|
||||
if (this.projectId == null) {
|
||||
this.errorMessage.set('Invalid project id.');
|
||||
return;
|
||||
}
|
||||
|
||||
const payload: CreateTaskRequest = {
|
||||
title: this.title.trim(),
|
||||
description: this.description.trim() ? this.description.trim() : undefined,
|
||||
status: this.status,
|
||||
};
|
||||
|
||||
this.isSaving.set(true);
|
||||
this.errorMessage.set('');
|
||||
|
||||
this.apiService.post<Task>(`/projects/${this.projectId}/tasks`, payload).subscribe({
|
||||
next: () => {
|
||||
this.isSaving.set(false);
|
||||
this.router.navigate(['/projects', this.projectId]);
|
||||
},
|
||||
error: (error) => {
|
||||
this.isSaving.set(false);
|
||||
this.errorMessage.set(error?.error?.message || 'Failed to create task.');
|
||||
},
|
||||
});
|
||||
}
|
||||
|
||||
onCancel() {
|
||||
if (this.projectId != null) {
|
||||
this.router.navigate(['/projects', this.projectId]);
|
||||
} else {
|
||||
this.router.navigate(['/']);
|
||||
}
|
||||
}
|
||||
}
|
||||
Loading…
Add table
Add a link
Reference in a new issue