mirror of
https://github.com/a-mayb3/KanbanCloneAngular.git
synced 2026-03-21 18:05:38 +01:00
Added completion status in task item
This commit is contained in:
parent
a7f506b873
commit
f7f12356de
4 changed files with 139 additions and 16 deletions
|
|
@ -3,11 +3,59 @@
|
||||||
border-radius: 10px;
|
border-radius: 10px;
|
||||||
padding: 16px;
|
padding: 16px;
|
||||||
display: flex;
|
display: flex;
|
||||||
flex-direction: column;
|
flex-direction: row;
|
||||||
gap: 12px;
|
align-items: center;
|
||||||
|
justify-content: space-between;
|
||||||
|
gap: 16px;
|
||||||
background-color: #f9fafb;
|
background-color: #f9fafb;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.task-meta {
|
||||||
|
display: flex;
|
||||||
|
flex-direction: row;
|
||||||
|
align-items: center;
|
||||||
|
flex-wrap: wrap;
|
||||||
|
gap: 6px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.status-label {
|
||||||
|
font-size: 12px;
|
||||||
|
font-weight: 600;
|
||||||
|
color: #4b5563;
|
||||||
|
text-transform: uppercase;
|
||||||
|
letter-spacing: 0.04em;
|
||||||
|
}
|
||||||
|
|
||||||
|
.status-select {
|
||||||
|
padding: 6px 10px;
|
||||||
|
border-radius: 8px;
|
||||||
|
border: 1px solid #e5e7eb;
|
||||||
|
background-color: #ffffff;
|
||||||
|
font-size: 12px;
|
||||||
|
color: #374151;
|
||||||
|
min-width: 140px;
|
||||||
|
height: 32px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.btn-remove {
|
||||||
|
width: 32px;
|
||||||
|
height: 32px;
|
||||||
|
border-radius: 8px;
|
||||||
|
border: 1px solid #fecaca;
|
||||||
|
background: #ffffff;
|
||||||
|
color: #b91c1c;
|
||||||
|
font-weight: 700;
|
||||||
|
cursor: pointer;
|
||||||
|
transition:
|
||||||
|
background-color 0.2s ease,
|
||||||
|
border-color 0.2s ease;
|
||||||
|
}
|
||||||
|
|
||||||
|
.btn-remove:hover {
|
||||||
|
background-color: #fee2e2;
|
||||||
|
border-color: #fca5a5;
|
||||||
|
}
|
||||||
|
|
||||||
.task-info h4 {
|
.task-info h4 {
|
||||||
margin: 0 0 6px 0;
|
margin: 0 0 6px 0;
|
||||||
color: #111827;
|
color: #111827;
|
||||||
|
|
@ -19,6 +67,10 @@
|
||||||
font-size: 14px;
|
font-size: 14px;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.task-info {
|
||||||
|
flex: 1;
|
||||||
|
}
|
||||||
|
|
||||||
.status {
|
.status {
|
||||||
align-self: flex-start;
|
align-self: flex-start;
|
||||||
padding: 4px 10px;
|
padding: 4px 10px;
|
||||||
|
|
|
||||||
|
|
@ -3,7 +3,20 @@
|
||||||
<h4>{{ task.title }}</h4>
|
<h4>{{ task.title }}</h4>
|
||||||
<p>{{ task.description || 'No description.' }}</p>
|
<p>{{ task.description || 'No description.' }}</p>
|
||||||
</div>
|
</div>
|
||||||
<span class="status" [ngClass]="'status-' + task.status.toLowerCase()">
|
<div class="task-meta">
|
||||||
{{ task.status.replace('_', ' ') }}
|
<label class="status-label" for="status-{{ task.id }}">Status</label>
|
||||||
</span>
|
<select
|
||||||
|
id="status-{{ task.id }}"
|
||||||
|
class="status-select"
|
||||||
|
[(ngModel)]="statusValue"
|
||||||
|
(ngModelChange)="onStatusChange($event)"
|
||||||
|
>
|
||||||
|
@for (status of statusOptions; track status) {
|
||||||
|
<option [value]="status">{{ status.replace('_', ' ') }}</option>
|
||||||
|
}
|
||||||
|
</select>
|
||||||
|
<button class="btn-remove" type="button" aria-label="Remove task" (click)="onRemove()">
|
||||||
|
x
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
</article>
|
</article>
|
||||||
|
|
|
||||||
|
|
@ -1,14 +1,62 @@
|
||||||
import { Component, Input } from '@angular/core';
|
import { Component, EventEmitter, Input, Output } from '@angular/core';
|
||||||
import { CommonModule } from '@angular/common';
|
import { CommonModule } from '@angular/common';
|
||||||
|
import { FormsModule } from '@angular/forms';
|
||||||
import { Task } from '../../models/tasks.models';
|
import { Task } from '../../models/tasks.models';
|
||||||
|
|
||||||
@Component({
|
@Component({
|
||||||
selector: 'app-task-item',
|
selector: 'app-task-item',
|
||||||
standalone: true,
|
standalone: true,
|
||||||
imports: [CommonModule],
|
imports: [CommonModule, FormsModule],
|
||||||
templateUrl: './task-item.component.html',
|
templateUrl: './task-item.component.html',
|
||||||
styleUrl: './task-item.component.css',
|
styleUrl: './task-item.component.css',
|
||||||
})
|
})
|
||||||
export class TaskItemComponent {
|
export class TaskItemComponent {
|
||||||
@Input({ required: true }) task!: Task;
|
private _task!: Task;
|
||||||
|
statusValue: Task['status'] = 'pending';
|
||||||
|
|
||||||
|
@Input({ required: true })
|
||||||
|
set task(value: Task) {
|
||||||
|
this._task = value;
|
||||||
|
this.statusValue = value?.status ?? 'pending';
|
||||||
|
}
|
||||||
|
|
||||||
|
get task(): Task {
|
||||||
|
return this._task;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Output() statusChange = new EventEmitter<{
|
||||||
|
task: Task;
|
||||||
|
status: Task['status'];
|
||||||
|
previousStatus: Task['status'];
|
||||||
|
}>();
|
||||||
|
|
||||||
|
@Output() remove = new EventEmitter<Task>();
|
||||||
|
|
||||||
|
readonly statusOptions: Task['status'][] = [
|
||||||
|
'pending',
|
||||||
|
'in_progress',
|
||||||
|
'completed',
|
||||||
|
'stashed',
|
||||||
|
'failed',
|
||||||
|
];
|
||||||
|
|
||||||
|
onStatusChange(value: Task['status']) {
|
||||||
|
const previousStatus = this.statusValue;
|
||||||
|
const nextStatus = value;
|
||||||
|
this.statusValue = nextStatus;
|
||||||
|
if (this._task) {
|
||||||
|
this._task.status = nextStatus;
|
||||||
|
this.statusChange.emit({
|
||||||
|
task: this._task,
|
||||||
|
status: nextStatus,
|
||||||
|
previousStatus,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
onRemove() {
|
||||||
|
if (this._task) {
|
||||||
|
this.remove.emit(this._task);
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,18 +1,19 @@
|
||||||
import { User } from "./auth.models";
|
import { User } from './auth.models';
|
||||||
import { Task } from "./tasks.models";
|
import { Task } from './tasks.models';
|
||||||
|
|
||||||
export interface Project {
|
export interface Project {
|
||||||
id: number;
|
id: number;
|
||||||
name: string;
|
name: string;
|
||||||
description?: string;
|
description?: string;
|
||||||
|
tasks?: Task[];
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface ProjectFull {
|
export interface ProjectFull {
|
||||||
id: number;
|
id: number;
|
||||||
name: string;
|
name: string;
|
||||||
description?: string;
|
description?: string;
|
||||||
tasks: Task[];
|
tasks: Task[];
|
||||||
users: User[];
|
users: User[];
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface CreateProjectRequest {
|
export interface CreateProjectRequest {
|
||||||
|
|
@ -24,3 +25,12 @@ export interface UpdateProjectRequest {
|
||||||
name?: string;
|
name?: string;
|
||||||
description?: string;
|
description?: string;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export interface AddCollaboratorRequest {
|
||||||
|
user_email: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface AddCollaboratorResponse {
|
||||||
|
success?: boolean;
|
||||||
|
message?: string;
|
||||||
|
}
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue