nikolaik 2 hete
szülő
commit
bd48ba8858

+ 3 - 1
src-angl/is/angular.json

@@ -2,7 +2,8 @@
   "$schema": "./node_modules/@angular/cli/lib/config/schema.json",
   "version": 1,
   "cli": {
-    "packageManager": "npm"
+    "packageManager": "npm",
+    "analytics": false
   },
   "newProjectRoot": "projects",
   "projects": {
@@ -25,6 +26,7 @@
               }
             ],
             "styles": [
+              "node_modules/@fortawesome/fontawesome-free/css/all.min.css",
               "src/styles.css"
             ]
           },

+ 16 - 6
src-angl/is/package-lock.json

@@ -14,6 +14,7 @@
         "@angular/forms": "^21.1.0",
         "@angular/platform-browser": "^21.1.0",
         "@angular/router": "^21.1.0",
+        "@fortawesome/fontawesome-free": "^7.1.0",
         "rxjs": "~7.8.0",
         "tslib": "^2.3.0"
       },
@@ -1588,6 +1589,15 @@
         }
       }
     },
+    "node_modules/@fortawesome/fontawesome-free": {
+      "version": "7.1.0",
+      "resolved": "https://registry.npmjs.org/@fortawesome/fontawesome-free/-/fontawesome-free-7.1.0.tgz",
+      "integrity": "sha512-+WxNld5ZCJHvPQCr/GnzCTVREyStrAJjisUPtUxG5ngDA8TMlPnKp6dddlTpai4+1GNmltAeuk1hJEkBohwZYA==",
+      "license": "(CC-BY-4.0 AND OFL-1.1 AND MIT)",
+      "engines": {
+        "node": ">=6"
+      }
+    },
     "node_modules/@hono/node-server": {
       "version": "1.19.9",
       "resolved": "https://registry.npmjs.org/@hono/node-server/-/node-server-1.19.9.tgz",
@@ -5499,9 +5509,9 @@
       }
     },
     "node_modules/hono": {
-      "version": "4.11.5",
-      "resolved": "https://registry.npmjs.org/hono/-/hono-4.11.5.tgz",
-      "integrity": "sha512-WemPi9/WfyMwZs+ZUXdiwcCh9Y+m7L+8vki9MzDw3jJ+W9Lc+12HGsd368Qc1vZi1xwW8BWMMsnK5efYKPdt4g==",
+      "version": "4.11.7",
+      "resolved": "https://registry.npmjs.org/hono/-/hono-4.11.7.tgz",
+      "integrity": "sha512-l7qMiNee7t82bH3SeyUCt9UF15EVmaBvsppY2zQtrbIhl/yzBTny+YUxsVjSjQ6gaqaeVtZmGocom8TzBlA4Yw==",
       "dev": true,
       "license": "MIT",
       "peer": true,
@@ -7887,9 +7897,9 @@
       "license": "MIT"
     },
     "node_modules/tar": {
-      "version": "7.5.6",
-      "resolved": "https://registry.npmjs.org/tar/-/tar-7.5.6.tgz",
-      "integrity": "sha512-xqUeu2JAIJpXyvskvU3uvQW8PAmHrtXp2KDuMJwQqW8Sqq0CaZBAQ+dKS3RBXVhU4wC5NjAdKrmh84241gO9cA==",
+      "version": "7.5.7",
+      "resolved": "https://registry.npmjs.org/tar/-/tar-7.5.7.tgz",
+      "integrity": "sha512-fov56fJiRuThVFXD6o6/Q354S7pnWMJIVlDBYijsTNx6jKSE4pvrDTs6lUnmGvNyfJwFQQwWy3owKz1ucIhveQ==",
       "dev": true,
       "license": "BlueOak-1.0.0",
       "dependencies": {

+ 2 - 1
src-angl/is/package.json

@@ -29,6 +29,7 @@
     "@angular/forms": "^21.1.0",
     "@angular/platform-browser": "^21.1.0",
     "@angular/router": "^21.1.0",
+    "@fortawesome/fontawesome-free": "^7.1.0",
     "rxjs": "~7.8.0",
     "tslib": "^2.3.0"
   },
@@ -40,4 +41,4 @@
     "typescript": "~5.9.2",
     "vitest": "^4.0.8"
   }
-}
+}

+ 11 - 0
src-angl/is/src/app/article-delete-button/article-delete-button.css

@@ -0,0 +1,11 @@
+.icon-btn {
+    background: none;
+    border: none;
+    padding: 0;
+    cursor: pointer;
+}
+
+.icon-btn:disabled {
+    cursor: not-allowed;
+    opacity: 0.5;
+}

+ 5 - 6
src-angl/is/src/app/article-delete-button/article-delete-button.html

@@ -1,7 +1,6 @@
-<button [disabled]="busy" (click)="delete()">
-	Artikel löschen
-
-	@if (busy) {
-		processing...
-	}
+<button class="icon-btn" [disabled]="busy" (click)="delete()">
+	<i
+			class="fa-solid"
+			[ngClass]="busy ? 'fa-spinner fa-spin' : 'fa-trash-can'">
+	</i>
 </button>

+ 8 - 3
src-angl/is/src/app/article-delete-button/article-delete-button.ts

@@ -1,24 +1,29 @@
 import { Component, EventEmitter, inject, Input, Output } from '@angular/core';
 import { Article } from '../bo/article';
 import { DataService } from '../data-service';
+import { NgClass } from '@angular/common';
 
 @Component({
 	selector: 'is-article-delete-button',
-	imports: [],
 	templateUrl: './article-delete-button.html',
+	styleUrl: './article-delete-button.css',
+	imports: [
+		NgClass
+	],
+
 })
 export class ArticleDeleteButton {
 	busy = false;
 
 	dataService = inject(DataService);
 
-	@Input({ required: true })
+	@Input({required: true})
 	article!: Article;
 
 	@Output()
 	deleted = new EventEmitter();
 
-	delete(){
+	delete() {
 		this.busy = true;
 		this.dataService.deleteArticle(this.article.id).subscribe(() => {
 			this.busy = false;

+ 6 - 0
src-angl/is/src/app/article-edit-button/article-edit-button.css

@@ -0,0 +1,6 @@
+.icon-btn {
+    background: none;
+    border: none;
+    padding: 0;
+    cursor: pointer;
+}

+ 3 - 0
src-angl/is/src/app/article-edit-button/article-edit-button.html

@@ -0,0 +1,3 @@
+<button class='icon-btn' [disabled]='busy' (click)="edit()">
+	<i class="fa-solid" [ngClass]="busy ? 'fa-spinner fa-spin' : 'fa-edit'"></i>
+</button>

+ 39 - 0
src-angl/is/src/app/article-edit-button/article-edit-button.ts

@@ -0,0 +1,39 @@
+import { Component, inject, Input } from '@angular/core';
+import { Article } from '../bo/article';
+import {  Router } from '@angular/router';
+import { NgClass } from '@angular/common';
+import { DataService } from '../data-service';
+import { ArticleEditState } from '../articleEditState';
+
+@Component({
+	selector: 'is-article-edit-button',
+	templateUrl: './article-edit-button.html',
+	styleUrl: './article-edit-button.css',
+	imports: [ NgClass]
+})
+export class ArticleEditButton {
+	busy = false;
+
+	@Input({required: true})
+	article!: Article;
+
+	private dataService = inject(DataService);
+	private router = inject(Router);
+	private editState = inject(ArticleEditState);
+
+	edit() {
+		if (this.busy) return;
+
+		this.busy = true;
+
+		this.dataService.getArticleById(this.article.id).subscribe({
+			next: article => {
+				this.editState.article = article;
+				this.router.navigate([`/edit/${article.id}`]);
+			},
+			error: () => {
+				this.busy = false;
+			}
+		});
+	}
+}

+ 1 - 5
src-angl/is/src/app/article-form-edit/article-form-edit.html

@@ -1,6 +1,2 @@
-@if (!articleInputSignal()) {
-	loading...
-} @else {
-	<is-article-form [title]="'Edit'" [submitButtonName]="'Update Article'" [articleInput]="articleInputSignal()!"
+<is-article-form [title]="'Edit'" [submitButtonName]="'Update Article'" [articleInput]="articleInputSignal()!"
 			(articleInputChange)="handleSubmit($event)"></is-article-form>
-}

+ 18 - 31
src-angl/is/src/app/article-form-edit/article-form-edit.ts

@@ -1,12 +1,9 @@
 import { Component, inject, OnInit, signal } from '@angular/core';
 import {  FormsModule, ReactiveFormsModule } from '@angular/forms';
-import { firstValueFrom, Observable } from 'rxjs';
-import { ArticleGroup } from '../bo/article-group';
-import { Article } from '../bo/article';
 import { DataService } from '../data-service';
-import { ActivatedRoute, Router } from '@angular/router';
+import {  Router } from '@angular/router';
 import { ArticleForm, ArticleInput } from '../article-form/article-form';
-
+import { ArticleEditState } from '../articleEditState';
 @Component({
 	selector: 'is-article-form-edit',
 	templateUrl: './article-form-edit.html',
@@ -28,46 +25,36 @@ export class ArticleFormEdit implements OnInit {
 	// 	this.update(id)
 	// }
 
-	articleToEditId!: number;
-	articleToEdit!: Article;
-	articleToEditArticleGroup!: ArticleGroup | null;
-
+	private editState = inject(ArticleEditState);
+	private dataService = inject(DataService);
+	articleToEdit!: number;
 	articleInputSignal = signal<ArticleInput|undefined>(undefined) ;
 
-	constructor(private route: ActivatedRoute, private router: Router) {
+	constructor( private router: Router) {
 
 	}
 
-	private dataService = inject(DataService);
-
 	ngOnInit() {
-		this.route.paramMap.subscribe((paramMap) => {
-			const id = Number(paramMap.get('id'));
-			if (isNaN(id)) {
-				this.cancel();
-			}
+		const article = this.editState.article;
 
-			this.articleToEditId = id;
-			this.update(id);
-		});
-	}
+		if (!article) {
+			this.cancel();
+			return;
+		}
 
-	private update(id: number) {
-		this.dataService.getArticleById(id).subscribe((article: Article) => {
-			this.articleToEdit = article;
+		this.articleToEdit = article.id;
 
-			this.articleInputSignal.set({
-				title: this.articleToEdit.title,
-				text: this.articleToEdit.text,
-				categoryName: this.articleToEdit.categoryName,
-				articleGroupId: this.articleToEdit.articleGroupId,
-			});
+		this.articleInputSignal.set({
+			title: article.title,
+			text: article.text,
+			categoryName: article.categoryName,
+			articleGroupId: article.articleGroupId,
 		});
 	}
 
 
 	handleSubmit(articleInput: ArticleInput) {
-		this.dataService.putArticle(this.articleToEditId, articleInput).subscribe(() => {
+		this.dataService.putArticle(this.articleToEdit, articleInput).subscribe(() => {
 			this.cancel();
 		});
 	}

+ 1 - 6
src-angl/is/src/app/article-form/article-form.ts

@@ -2,10 +2,9 @@ import { Component, EventEmitter, inject, Input, OnInit, Output } from '@angular
 import { AsyncPipe } from '@angular/common';
 import { DataService } from '../data-service';
 import { Observable } from 'rxjs';
-import { Article, CategoryName } from '../bo/article';
+import {  CategoryName } from '../bo/article';
 import { ArticleGroup } from '../bo/article-group';
 import { FormControl, FormGroup, FormsModule, ReactiveFormsModule, Validators } from '@angular/forms';
-import { Router } from '@angular/router';
 
 @Component({
 	selector: 'is-article-form',
@@ -42,10 +41,6 @@ export class ArticleForm implements OnInit {
 
 	articleGroups$?: Observable<ArticleGroup[]>;
 	categoryNames: CategoryName[] = Object.values(CategoryName);
-
-	constructor(private router: Router) {
-	}
-
 	ngOnInit() {
 		this.articleForm.patchValue({
 			title: this.articleInput.title,

+ 7 - 0
src-angl/is/src/app/articleEditState.ts

@@ -0,0 +1,7 @@
+import { Injectable } from '@angular/core';
+import { Article } from './bo/article';
+
+@Injectable({ providedIn: 'root' })
+export class ArticleEditState {
+	article?: Article;
+}

+ 2 - 3
src-angl/is/src/app/articles/articles.html

@@ -24,9 +24,8 @@
 				<td>{{ article.articleGroupId === null ? "None" : article.articleGroupId }}</td>
 				<td>
 					<div class="modify-buttons">
-						<span routerLink="edit/{{ article.id }}">Edit</span>
-						<is-article-delete-button [article]="article" (deleted)="articleDeleted(article)">
-						</is-article-delete-button>
+						<is-article-edit-button [article]="article"/>
+						<is-article-delete-button [article]="article" (deleted)="articleDeleted(article)" />
 					</div>
 				</td>
 			</tr>

+ 12 - 15
src-angl/is/src/app/articles/articles.ts

@@ -1,32 +1,29 @@
-import { ChangeDetectorRef, Component, inject, OnInit, signal } from '@angular/core';
-import {DataService} from '../data-service';
-import { Observable } from 'rxjs';
+import {Component, inject, OnInit, signal } from '@angular/core';
+import { DataService } from '../data-service';
 import { Article } from '../bo/article';
 import { FloatButton } from '../float-button/float-button';
-import { RouterLink } from '@angular/router';
 import { ArticleDeleteButton } from '../article-delete-button/article-delete-button';
+import { ArticleEditButton } from '../article-edit-button/article-edit-button';
+
 @Component({
 	selector: 'is-app-articles',
 	templateUrl: 'articles.html',
 	styleUrl: 'articles.css',
-	imports: [FloatButton, RouterLink, ArticleDeleteButton],
+	imports: [FloatButton, ArticleDeleteButton, ArticleEditButton],
 })
 
 export class Articles implements OnInit {
-    private dataService = inject(DataService);
-
-	articlesSignal = signal<Article[]|undefined>(undefined)
+	private dataService = inject(DataService);
 
-	constructor(private cdr: ChangeDetectorRef) {
-	}
+	articlesSignal = signal<Article[] | undefined>(undefined);
 
-    ngOnInit() {
-      	this.dataService.getArticles().subscribe((articles) => {
-		  	this.articlesSignal.set(articles);
+	ngOnInit() {
+		this.dataService.getArticles().subscribe((articles) => {
+			this.articlesSignal.set(articles);
 		});
-    }
+	}
 
-	 articleDeleted(article: Article){
+	articleDeleted(article: Article) {
 		const articles = this.articlesSignal();
 		if (!articles) {
 			return;

+ 9 - 8
src-angl/is/src/app/float-button/float-button.css

@@ -1,15 +1,16 @@
+
+.float-button-container{
+    display: flex;
+    justify-content: end;
+    margin-top: 2rem;
+}
+
 .float-button {
     border: 1px solid black;
     border-radius: 50%;
-    width: fit-content;
-    padding: 0.5rem 0.8rem;
+    width: 30px;
+    height: 30px;
     background-color: turquoise;
     color: black;
     cursor: pointer;
 }
-
-.float-button-container{
-    display: flex;
-    justify-content: end;
-    margin-top: 2rem;
-}

+ 3 - 1
src-angl/is/src/app/float-button/float-button.html

@@ -1,4 +1,6 @@
 
 <div class="float-button-container">
-	<div class="float-button" routerLink="new">+</div>
+	<button class="float-button" routerLink="new">
+		<i class="fa-solid fa-plus"></i>
+	</button>
 </div>