@ichgamer999/wmctest 1.1.0 → 1.1.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@ichgamer999/wmctest",
3
- "version": "1.1.0",
3
+ "version": "1.1.1",
4
4
  "description": "Minimalist Angular authentication template with signals, JWT token handling, and HTTP interceptors",
5
5
  "keywords": [
6
6
  "angular",
@@ -0,0 +1,23 @@
1
+ .layout {
2
+ display: flex;
3
+ min-height: 100vh;
4
+ }
5
+
6
+ .sidebar {
7
+ width: 180px;
8
+ padding: 8px;
9
+ background: #e0e0e0;
10
+ display: flex;
11
+ flex-direction: column;
12
+ gap: 8px;
13
+ }
14
+
15
+ .content {
16
+ flex: 1;
17
+ padding: 8px;
18
+ }
19
+
20
+ button {
21
+ width: 100%;
22
+ }
23
+
@@ -0,0 +1,19 @@
1
+ <div class="layout">
2
+ <div class="sidebar">
3
+ @if (auth.isAuthenticated()) {
4
+ <div>{{ auth.getUserName() }}</div>
5
+ }
6
+
7
+ <a href="/expenses">Expenses</a>
8
+
9
+ @if (!auth.isAuthenticated()) {
10
+ <button (click)="login()">Login</button>
11
+ } @else {
12
+ <button (click)="logout()">LogOut</button>
13
+ }
14
+ </div>
15
+
16
+ <div class="content">
17
+ <router-outlet></router-outlet>
18
+ </div>
19
+ </div>
@@ -0,0 +1,26 @@
1
+ import {Component, inject} from '@angular/core';
2
+ import {LoginComponent} from "./components/login/login.component";
3
+ import {Router, RouterOutlet} from "@angular/router";
4
+ import {AuthService} from "./services/auth.service";
5
+ import {HttpServiceService} from "./services/http-service.service";
6
+
7
+ @Component({
8
+ selector: 'app-root',
9
+ imports: [
10
+ RouterOutlet
11
+ ],
12
+ templateUrl: './app.component.html',
13
+ styleUrl: './app.component.css'
14
+ })
15
+ export class AppComponent {
16
+ auth = inject(AuthService)
17
+ router = inject(Router);
18
+ http = inject(HttpServiceService)
19
+ login(){
20
+ this.router.navigate(['/login']);
21
+ }
22
+
23
+ logout(){
24
+ this.http.logout()
25
+ }
26
+ }
@@ -0,0 +1,12 @@
1
+ import {ApplicationConfig, provideZonelessChangeDetection} from '@angular/core';
2
+ import {provideRouter} from '@angular/router';
3
+
4
+ import {routes} from './app.routes';
5
+ import {provideHttpClient, withInterceptors} from "@angular/common/http";
6
+ import {authInterceptor} from "./interceptor/auth.interceptor";
7
+
8
+ export const appConfig: ApplicationConfig = {
9
+ providers: [provideZonelessChangeDetection(),
10
+ provideHttpClient(withInterceptors([ authInterceptor])),
11
+ provideRouter(routes)]
12
+ };
@@ -0,0 +1,9 @@
1
+ import { Routes } from '@angular/router';
2
+ import {LoginComponent} from "./components/login/login.component";
3
+ import {ExpenseComponent} from "./components/expense/expense.component";
4
+
5
+ export const routes: Routes = [
6
+ { path: '', redirectTo: 'login', pathMatch: 'full' },
7
+ { path: 'login', component: LoginComponent },
8
+ { path: 'expenses', component: ExpenseComponent}
9
+ ];
@@ -0,0 +1,8 @@
1
+ .expense-row {
2
+ border: 1px solid #aaa;
3
+ padding: 6px;
4
+ margin-bottom: 6px;
5
+ }
6
+
7
+
8
+
@@ -0,0 +1,42 @@
1
+ <h2>My Expenses</h2>
2
+
3
+ @if (editingExpense(); as expenseToEdit) {
4
+ <form (ngSubmit)="saveEdit()">
5
+ <h3>Edit Expense</h3>
6
+ <div>
7
+ <label for="type">Type</label>
8
+ <input id="type" name="type" [(ngModel)]="expenseToEdit.type" />
9
+ </div>
10
+ <div>
11
+ <label for="description">Description</label>
12
+ <input id="description" name="description" [(ngModel)]="expenseToEdit.description" />
13
+ </div>
14
+ <div>
15
+ <label for="amount">Amount</label>
16
+ <input id="amount" name="amount" type="number" [(ngModel)]="expenseToEdit.amount" />
17
+ </div>
18
+ <div>
19
+ <label for="expenseDate">Date</label>
20
+ <input id="expenseDate" name="expenseDate" type="date" [(ngModel)]="expenseToEdit.expenseDate" />
21
+ </div>
22
+ <div>
23
+ <button type="submit">Save</button>
24
+ <button type="button" (click)="cancelEdit()">Cancel</button>
25
+ </div>
26
+ </form>
27
+ }
28
+
29
+ @for (expense of this.expenses(); track expense.clientId ?? $index) {
30
+ <div class="expense-row">
31
+
32
+
33
+ <div [style.color]="isFoodType(expense.type)">{{ expense.type }}</div>
34
+ <div>{{ expense.description }}</div>
35
+ <div>${{ expense.amount }}</div>
36
+ <div>{{ expense.expenseDate }}</div>
37
+ <div>
38
+ <button type="button" (click)="startEdit(expense)">Edit</button>
39
+ <button type="button" (click)="deleteExpense(expense)">Delete</button>
40
+ </div>
41
+ </div>
42
+ }
@@ -0,0 +1,97 @@
1
+ import {Component, inject, OnInit, signal} from '@angular/core';
2
+ import {AuthService} from "../../services/auth.service";
3
+ import {HttpServiceService} from "../../services/http-service.service";
4
+ import {Expense} from "../../types/Expense";
5
+ import {FormsModule} from "@angular/forms";
6
+ import {Router} from "@angular/router";
7
+
8
+ @Component({
9
+ selector: 'app-expense',
10
+ imports: [
11
+ FormsModule
12
+ ],
13
+ templateUrl: './expense.component.html',
14
+ styleUrl: './expense.component.css',
15
+ })
16
+ export class ExpenseComponent implements OnInit {
17
+
18
+ authService = inject(AuthService)
19
+ http = inject(HttpServiceService)
20
+ expenses = signal<Expense[]>([])
21
+ editingExpense = signal<Expense | null>(null)
22
+ editingExpenseId = signal<number | null>(null)
23
+ private nextClientId = 0;
24
+ router = inject(Router);
25
+
26
+ ngOnInit(): void {
27
+ if (this.authService.isAuthenticated()) {
28
+ this.http.getAllExpenses().subscribe(
29
+ value => {
30
+ this.expenses.set(
31
+ value.map(expense => ({
32
+ ...expense,
33
+ clientId: this.nextClientId++,
34
+ }))
35
+ )
36
+ }
37
+ )
38
+ } else {
39
+ this.router.navigate(["/login"])
40
+ }
41
+ }
42
+
43
+ startEdit(expense: Expense) {
44
+ this.editingExpenseId.set(expense.clientId ?? null)
45
+ this.editingExpense.set({ ...expense })
46
+ }
47
+
48
+ saveEdit() {
49
+ const expenseId = this.editingExpenseId()
50
+ const expense = this.editingExpense()
51
+
52
+ if (expenseId === null || !expense) {
53
+ return
54
+ }
55
+
56
+ this.expenses.update(currentExpenses =>
57
+ currentExpenses.map(currentExpense =>
58
+ currentExpense.clientId === expenseId ? { ...currentExpense, ...expense, clientId: expenseId } : currentExpense
59
+ )
60
+ )
61
+
62
+ this.cancelEdit()
63
+ }
64
+
65
+ deleteExpense(expense: Expense) {
66
+ const expenseId = expense.clientId
67
+
68
+ if (expenseId === undefined) {
69
+ return
70
+ }
71
+
72
+ const confirmed = window.confirm(`Möchtest du ${expense.type} wirklich löschen?`)
73
+
74
+ if (!confirmed) {
75
+ return
76
+ }
77
+
78
+ this.expenses.update(currentExpenses => currentExpenses.filter(currentExpense => currentExpense.clientId !== expenseId))
79
+
80
+ if (this.editingExpenseId() === expenseId) {
81
+ this.cancelEdit()
82
+ }
83
+ }
84
+
85
+ cancelEdit() {
86
+ this.editingExpenseId.set(null)
87
+ this.editingExpense.set(null)
88
+ }
89
+
90
+ isFoodType(type: string | undefined) {
91
+ if (type?.toLowerCase() == 'food') {
92
+ return 'green'
93
+ }
94
+ else return 'blue'
95
+ }
96
+
97
+ }
@@ -0,0 +1,24 @@
1
+ form {
2
+ max-width: 320px;
3
+ margin: 16px 0;
4
+ padding: 8px;
5
+ border: 1px solid #999;
6
+ }
7
+
8
+ label {
9
+ display: block;
10
+ margin-bottom: 8px;
11
+ }
12
+
13
+ input {
14
+ width: 100%;
15
+ margin-top: 2px;
16
+ margin-bottom: 8px;
17
+ border: 1px solid #999;
18
+ }
19
+
20
+ button {
21
+ width: 100%;
22
+ border: 1px solid #666;
23
+ background: #d9d9d9;
24
+ }
@@ -0,0 +1,13 @@
1
+ <form [formGroup]="loginModel" (ngSubmit)="onSubmit()">
2
+
3
+ <label>
4
+ Username
5
+ <input type="text" formControlName="username" />
6
+ </label>
7
+ <label>
8
+ Password
9
+ <input type="password" formControlName="password" />
10
+ </label>
11
+ <button type="submit" [disabled]="loginModel.invalid">Login</button>
12
+
13
+ </form>
@@ -0,0 +1,34 @@
1
+ import {Component, inject, signal} from '@angular/core';
2
+ import {FormControl, FormGroup, FormsModule, ReactiveFormsModule, Validators} from "@angular/forms";
3
+ import {HttpServiceService} from "../../services/http-service.service";
4
+ import {Router} from "@angular/router";
5
+
6
+ @Component({
7
+ selector: 'app-login',
8
+ imports: [
9
+ FormsModule,
10
+ ReactiveFormsModule
11
+ ],
12
+ templateUrl: './login.component.html',
13
+ styleUrl: './login.component.css',
14
+ })
15
+ export class LoginComponent {
16
+ http = inject(HttpServiceService);
17
+ router = inject(Router);
18
+ loginModel = new FormGroup({
19
+ 'username': new FormControl('', Validators.required),
20
+ 'password': new FormControl('', Validators.required),
21
+ })
22
+
23
+ onSubmit() {
24
+ if (this.loginModel.valid) {
25
+ const {username, password} = this.loginModel.value;
26
+
27
+ if (username && password) {
28
+ this.http.login(username, password).subscribe({
29
+ next: () => this.router.navigate(['/expenses']),
30
+ });
31
+ }
32
+ }
33
+ }
34
+ }
@@ -0,0 +1,14 @@
1
+ import { HttpInterceptorFn } from '@angular/common/http';
2
+ import {inject} from "@angular/core";
3
+ import {AuthService} from "../services/auth.service";
4
+
5
+ export const authInterceptor: HttpInterceptorFn = (req, next) => {
6
+ const authService = inject(AuthService)
7
+ if(!authService.isAuthenticated()){
8
+ return next(req);
9
+ }
10
+ let clone = req.clone({
11
+ headers: req.headers.set('Authorization', `Bearer ${authService.getToken()}`)
12
+ });
13
+ return next(clone);
14
+ };
@@ -0,0 +1,31 @@
1
+ import { Injectable } from '@angular/core';
2
+
3
+ @Injectable({
4
+ providedIn: 'root',
5
+ })
6
+ export class AuthService {
7
+ private is_authenticated = "";
8
+
9
+ constructor() {
10
+ const savedUser = localStorage.getItem("userName");
11
+ if (savedUser) {
12
+ this.is_authenticated = savedUser;
13
+ }
14
+ }
15
+
16
+ isAuthenticated(){
17
+ return this.is_authenticated != "";
18
+ }
19
+
20
+ setAuthenticated(newState: string){
21
+ this.is_authenticated = newState;
22
+ }
23
+
24
+ getUserName(){
25
+ return this.is_authenticated
26
+ }
27
+
28
+ getToken(){
29
+ return localStorage.getItem('secretAuthToken');
30
+ }
31
+ }
@@ -0,0 +1,48 @@
1
+ import {inject, Injectable} from '@angular/core';
2
+ import {HttpClient} from "@angular/common/http";
3
+ import {AuthService} from "./auth.service";
4
+ import {Router} from "@angular/router";
5
+ import {Observable, tap} from "rxjs";
6
+ import {Expense} from "../types/Expense";
7
+
8
+ @Injectable({
9
+ providedIn: 'root',
10
+ })
11
+ export class HttpServiceService {
12
+ http = inject(HttpClient);
13
+ authService = inject(AuthService);
14
+ router = inject(Router);
15
+ private baseURL = "http://localhost:8080/api";
16
+
17
+ login(username: string, password: string){
18
+ return this.http
19
+ .post<{ token: string }>(`${this.baseURL}/token`,
20
+ { username: username, password: password })
21
+ .pipe(
22
+ tap(response => {
23
+ localStorage.setItem('secretAuthToken', response.token);
24
+ localStorage.setItem("userName", username)
25
+ this.authService.setAuthenticated(username);
26
+ })
27
+ );
28
+ }
29
+
30
+ getAllExpenses(): Observable<Expense[]> {
31
+ return this.http.get<Expense[]>(`${this.baseURL}/expenses`);
32
+ }
33
+
34
+ updateExpense(expenseId: number, expense: Expense): Observable<Expense> {
35
+ return this.http.put<Expense>(`${this.baseURL}/expenses/${expenseId}`, expense);
36
+ }
37
+
38
+ deleteExpense(expenseId: number): Observable<void> {
39
+ return this.http.delete<void>(`${this.baseURL}/expenses/${expenseId}`);
40
+ }
41
+
42
+ logout(){
43
+ localStorage.removeItem('secretAuthToken');
44
+ this.authService.setAuthenticated("");
45
+ this.router.navigate(['/login']);
46
+ }
47
+
48
+ }
@@ -0,0 +1,18 @@
1
+ export class Expense {
2
+ id?: number
3
+ clientId?: number
4
+ type: string
5
+ amount: number
6
+ expenseDate: string
7
+ description: string
8
+
9
+
10
+ constructor(type: string, amount: number, expenseDate: string, description: string, id?: number, clientId?: number) {
11
+ this.id = id;
12
+ this.clientId = clientId;
13
+ this.type = type;
14
+ this.amount = amount;
15
+ this.expenseDate = expenseDate;
16
+ this.description = description;
17
+ }
18
+ }