feat: create, design and integrate LoadingSpinnerComponent

This commit is contained in:
Chneemann 2025-03-22 20:35:47 +01:00
parent 18903e3c55
commit 518a688272
8 changed files with 90 additions and 7 deletions

View file

@ -61,8 +61,12 @@
<!-- Show empty state if no 'todo' tasks exist --> <!-- Show empty state if no 'todo' tasks exist -->
} @empty { } @empty {
<!-- Loading spinner or empty task -->
@if (isLoading) {
<app-loading-spinner></app-loading-spinner>
} @else {
<app-task-empty>{{ "board.noTasks" | translate }}</app-task-empty> <app-task-empty>{{ "board.noTasks" | translate }}</app-task-empty>
} } }
<!-- Highlight task if just moved to 'todo' --> <!-- Highlight task if just moved to 'todo' -->
@if (taskMovedTo === TODO && taskMovedFrom !== TODO) { @if (taskMovedTo === TODO && taskMovedFrom !== TODO) {
@ -90,8 +94,12 @@
<!-- Show empty state if no 'in progress' tasks exist --> <!-- Show empty state if no 'in progress' tasks exist -->
} @empty { } @empty {
<!-- Loading spinner or empty task -->
@if (isLoading) {
<app-loading-spinner></app-loading-spinner>
} @else {
<app-task-empty>{{ "board.noTasks" | translate }}</app-task-empty> <app-task-empty>{{ "board.noTasks" | translate }}</app-task-empty>
} } }
<!-- Highlight task if just moved to 'in progress' --> <!-- Highlight task if just moved to 'in progress' -->
@if (taskMovedTo === IN_PROGRESS && taskMovedFrom !== IN_PROGRESS) { @if (taskMovedTo === IN_PROGRESS && taskMovedFrom !== IN_PROGRESS) {
@ -119,8 +127,12 @@
<!-- Show empty state if no 'await feedback' tasks exist --> <!-- Show empty state if no 'await feedback' tasks exist -->
} @empty { } @empty {
<!-- Loading spinner or empty task -->
@if (isLoading) {
<app-loading-spinner></app-loading-spinner>
} @else {
<app-task-empty>{{ "board.noTasks" | translate }}</app-task-empty> <app-task-empty>{{ "board.noTasks" | translate }}</app-task-empty>
} } }
<!-- Highlight task if just moved to 'await feedback' --> <!-- Highlight task if just moved to 'await feedback' -->
@if (taskMovedTo === AWAIT_FEEDBACK && taskMovedFrom !== @if (taskMovedTo === AWAIT_FEEDBACK && taskMovedFrom !==
@ -149,8 +161,12 @@
<!-- Show empty state if no 'done' tasks exist --> <!-- Show empty state if no 'done' tasks exist -->
} @empty { } @empty {
<!-- Loading spinner or empty task -->
@if (isLoading) {
<app-loading-spinner></app-loading-spinner>
} @else {
<app-task-empty>{{ "board.noTasks" | translate }}</app-task-empty> <app-task-empty>{{ "board.noTasks" | translate }}</app-task-empty>
} } }
<!-- Highlight task if just moved to 'done' --> <!-- Highlight task if just moved to 'done' -->
@if (taskMovedTo === DONE && taskMovedFrom !== DONE) { @if (taskMovedTo === DONE && taskMovedFrom !== DONE) {

View file

@ -12,6 +12,7 @@ import { TaskHighlightedComponent } from './task/task-highlighted/task-highlight
import { ApiService } from '../../services/api.service'; import { ApiService } from '../../services/api.service';
import { Task } from '../../interfaces/task.interface'; import { Task } from '../../interfaces/task.interface';
import { TaskService } from '../../services/task.service'; import { TaskService } from '../../services/task.service';
import { LoadingSpinnerComponent } from '../../shared/components/loading-spinner/loading-spinner.component';
@Component({ @Component({
selector: 'app-board', selector: 'app-board',
@ -23,6 +24,7 @@ import { TaskService } from '../../services/task.service';
FormsModule, FormsModule,
TranslateModule, TranslateModule,
TaskHighlightedComponent, TaskHighlightedComponent,
LoadingSpinnerComponent,
], ],
templateUrl: './board.component.html', templateUrl: './board.component.html',
styleUrl: './board.component.scss', styleUrl: './board.component.scss',
@ -49,6 +51,7 @@ export class BoardComponent {
searchInput: boolean = false; searchInput: boolean = false;
taskMovedTo: string = ''; taskMovedTo: string = '';
taskMovedFrom: string = ''; taskMovedFrom: string = '';
isLoading = false;
/** /**
* Is called when the component is initialized. * Is called when the component is initialized.
@ -62,15 +65,19 @@ export class BoardComponent {
/** /**
* Retrieves all tasks from the API and initializes the `allTasks` and `filteredTasks` properties. * Retrieves all tasks from the API and initializes the `allTasks` and `filteredTasks` properties.
*/ */
loadTasks(): void { loadTasks(): void {
this.isLoading = true;
this.taskService.loadAllTasks().subscribe({ this.taskService.loadAllTasks().subscribe({
next: (result) => { next: (result) => {
this.allTasks = result.allTasks; this.allTasks = result.allTasks;
this.filteredTasks = result.filteredTasks; this.filteredTasks = result.filteredTasks;
this.isLoading = false;
}, },
error: (err) => { error: (err) => {
console.error('Error loading the tasks:', err); console.error('Error loading the tasks:', err);
this.isLoading = false;
}, },
}); });
} }

View file

@ -92,7 +92,7 @@ section {
.subtask-line { .subtask-line {
display: flex; display: flex;
width: 120px; width: 144px;
height: 8px; height: 8px;
padding-right: 0px; padding-right: 0px;
flex-direction: column; flex-direction: column;
@ -100,7 +100,6 @@ section {
align-items: flex-start; align-items: flex-start;
border-radius: 8px; border-radius: 8px;
background-color: var(--very-light-gray); background-color: var(--very-light-gray);
margin-right: 11px;
} }
.filler-full { .filler-full {

View file

@ -0,0 +1,3 @@
<div class="loading-spinner">
<img src="assets/img/sync.svg" alt="sync" />
</div>

View file

@ -0,0 +1,22 @@
.loading-spinner {
display: flex;
justify-content: center;
align-items: center;
width: 100%;
height: 120px;
img {
height: 100px;
width: 100px;
animation: spin 2s linear infinite;
}
}
@keyframes spin {
0% {
transform: rotate(0deg);
}
100% {
transform: rotate(360deg);
}
}

View file

@ -0,0 +1,23 @@
import { ComponentFixture, TestBed } from '@angular/core/testing';
import { LoadingSpinnerComponent } from './loading-spinner.component';
describe('LoadingSpinnerComponent', () => {
let component: LoadingSpinnerComponent;
let fixture: ComponentFixture<LoadingSpinnerComponent>;
beforeEach(async () => {
await TestBed.configureTestingModule({
imports: [LoadingSpinnerComponent]
})
.compileComponents();
fixture = TestBed.createComponent(LoadingSpinnerComponent);
component = fixture.componentInstance;
fixture.detectChanges();
});
it('should create', () => {
expect(component).toBeTruthy();
});
});

View file

@ -0,0 +1,12 @@
import { Component } from '@angular/core';
@Component({
selector: 'app-loading-spinner',
standalone: true,
imports: [],
templateUrl: './loading-spinner.component.html',
styleUrl: './loading-spinner.component.scss'
})
export class LoadingSpinnerComponent {
}

1
src/assets/img/sync.svg Normal file
View file

@ -0,0 +1 @@
<svg xmlns="http://www.w3.org/2000/svg" height="24px" viewBox="0 -960 960 960" width="24px" fill="#e8eaed"><path d="M160-160v-80h110l-16-14q-52-46-73-105t-21-119q0-111 66.5-197.5T400-790v84q-72 26-116 88.5T240-478q0 45 17 87.5t53 78.5l10 10v-98h80v240H160Zm400-10v-84q72-26 116-88.5T720-482q0-45-17-87.5T650-648l-10-10v98h-80v-240h240v80H690l16 14q49 49 71.5 106.5T800-482q0 111-66.5 197.5T560-170Z"/></svg>

After

Width:  |  Height:  |  Size: 407 B