From b7a8627bcfa64d2611e08f4d7963f35956e07400 Mon Sep 17 00:00:00 2001
From: Phan Huy Tran
Date: Wed, 28 May 2025 12:50:54 +0200
Subject: [PATCH 1/7] test: dice service
---
.../casino/blackjack/BlackJackGameEntity.java | 2 -
.../java/de/szut/casino/dice/DiceDto.java | 2 +
.../java/de/szut/casino/dice/DiceService.java | 5 +-
.../de/szut/casino/dice/DiceServiceTest.java | 216 ++++++++++++++++++
4 files changed, 221 insertions(+), 4 deletions(-)
create mode 100644 backend/src/test/java/de/szut/casino/dice/DiceServiceTest.java
diff --git a/backend/src/main/java/de/szut/casino/blackjack/BlackJackGameEntity.java b/backend/src/main/java/de/szut/casino/blackjack/BlackJackGameEntity.java
index c9f57d7..4f22c9d 100644
--- a/backend/src/main/java/de/szut/casino/blackjack/BlackJackGameEntity.java
+++ b/backend/src/main/java/de/szut/casino/blackjack/BlackJackGameEntity.java
@@ -4,8 +4,6 @@ import com.fasterxml.jackson.annotation.JsonIgnore;
import com.fasterxml.jackson.annotation.JsonManagedReference;
import de.szut.casino.user.UserEntity;
import jakarta.persistence.*;
-import jakarta.validation.constraints.NotNull;
-import jakarta.validation.constraints.Positive;
import lombok.AllArgsConstructor;
import lombok.Getter;
import lombok.NoArgsConstructor;
diff --git a/backend/src/main/java/de/szut/casino/dice/DiceDto.java b/backend/src/main/java/de/szut/casino/dice/DiceDto.java
index ecbf3d7..f0caf48 100644
--- a/backend/src/main/java/de/szut/casino/dice/DiceDto.java
+++ b/backend/src/main/java/de/szut/casino/dice/DiceDto.java
@@ -5,12 +5,14 @@ import jakarta.validation.constraints.DecimalMax;
import jakarta.validation.constraints.DecimalMin;
import jakarta.validation.constraints.NotNull;
import lombok.Getter;
+import lombok.NoArgsConstructor;
import lombok.Setter;
import java.math.BigDecimal;
@Getter
@Setter
+@NoArgsConstructor
public class DiceDto extends BetDto {
private boolean rollOver;
diff --git a/backend/src/main/java/de/szut/casino/dice/DiceService.java b/backend/src/main/java/de/szut/casino/dice/DiceService.java
index 71e4584..836620b 100644
--- a/backend/src/main/java/de/szut/casino/dice/DiceService.java
+++ b/backend/src/main/java/de/szut/casino/dice/DiceService.java
@@ -11,10 +11,11 @@ import java.util.Random;
@Service
public class DiceService {
private static final int MAX_DICE_VALUE = 100;
- private final Random random = new Random();
+ private final Random random;
private final BalanceService balanceService;
- public DiceService(BalanceService balanceService) {
+ public DiceService(Random random, BalanceService balanceService) {
+ this.random = random;
this.balanceService = balanceService;
}
diff --git a/backend/src/test/java/de/szut/casino/dice/DiceServiceTest.java b/backend/src/test/java/de/szut/casino/dice/DiceServiceTest.java
new file mode 100644
index 0000000..21ca6cf
--- /dev/null
+++ b/backend/src/test/java/de/szut/casino/dice/DiceServiceTest.java
@@ -0,0 +1,216 @@
+package de.szut.casino.dice;
+
+import de.szut.casino.shared.service.BalanceService;
+import de.szut.casino.user.UserEntity;
+import org.junit.jupiter.api.BeforeEach;
+import org.junit.jupiter.api.Test;
+import org.junit.jupiter.api.extension.ExtendWith;
+import org.mockito.InjectMocks;
+import org.mockito.Mock;
+import org.mockito.junit.jupiter.MockitoExtension;
+
+import java.math.BigDecimal;
+import java.util.Random;
+
+import static org.junit.jupiter.api.Assertions.*;
+import static org.mockito.Mockito.*;
+
+@ExtendWith(MockitoExtension.class)
+class DiceServiceTest {
+
+ @Mock
+ private BalanceService balanceService;
+
+ @Mock
+ private Random random;
+
+ @InjectMocks
+ private DiceService diceService;
+
+ private UserEntity user;
+ private DiceDto diceDto;
+
+ @BeforeEach
+ void setUp() {
+ user = new UserEntity();
+ user.setId(1L);
+ user.setBalance(BigDecimal.valueOf(1000));
+
+ diceDto = new DiceDto();
+ diceDto.setBetAmount(BigDecimal.valueOf(10));
+ diceDto.setTargetValue(BigDecimal.valueOf(50));
+ diceDto.setRollOver(true);
+ }
+
+ @Test
+ void play_rollOver_win() {
+ diceDto.setRollOver(true);
+ diceDto.setTargetValue(BigDecimal.valueOf(50));
+ when(random.nextInt(anyInt())).thenReturn(55);
+
+ DiceResult result = diceService.play(user, diceDto);
+
+ assertTrue(result.isWin());
+ assertEquals(BigDecimal.valueOf(56), result.getRolledValue());
+ verify(balanceService, times(1)).subtractFunds(user, diceDto.getBetAmount());
+ verify(balanceService, times(1)).addFunds(eq(user), any(BigDecimal.class));
+ }
+
+ @Test
+ void play_rollOver_lose() {
+ diceDto.setRollOver(true);
+ diceDto.setTargetValue(BigDecimal.valueOf(50));
+ when(random.nextInt(anyInt())).thenReturn(49);
+
+ DiceResult result = diceService.play(user, diceDto);
+
+ assertFalse(result.isWin());
+ assertEquals(BigDecimal.valueOf(50), result.getRolledValue());
+ assertEquals(BigDecimal.ZERO, result.getPayout());
+ verify(balanceService, times(1)).subtractFunds(user, diceDto.getBetAmount());
+ verify(balanceService, never()).addFunds(eq(user), any(BigDecimal.class));
+ }
+
+ @Test
+ void play_rollUnder_win() {
+ diceDto.setRollOver(false);
+ diceDto.setTargetValue(BigDecimal.valueOf(50));
+ when(random.nextInt(anyInt())).thenReturn(48);
+
+ DiceResult result = diceService.play(user, diceDto);
+
+ assertTrue(result.isWin());
+ assertEquals(BigDecimal.valueOf(49), result.getRolledValue());
+ verify(balanceService, times(1)).subtractFunds(user, diceDto.getBetAmount());
+ verify(balanceService, times(1)).addFunds(eq(user), any(BigDecimal.class));
+ }
+
+ @Test
+ void play_rollUnder_lose() {
+ diceDto.setRollOver(false);
+ diceDto.setTargetValue(BigDecimal.valueOf(50));
+ when(random.nextInt(anyInt())).thenReturn(50);
+
+ DiceResult result = diceService.play(user, diceDto);
+
+ assertFalse(result.isWin());
+ assertEquals(BigDecimal.valueOf(51), result.getRolledValue());
+ assertEquals(BigDecimal.ZERO, result.getPayout());
+ verify(balanceService, times(1)).subtractFunds(user, diceDto.getBetAmount());
+ verify(balanceService, never()).addFunds(eq(user), any(BigDecimal.class));
+ }
+
+ @Test
+ void play_rollOver_targetValueOne_lose() {
+ diceDto.setRollOver(true);
+ diceDto.setTargetValue(BigDecimal.valueOf(1));
+ when(random.nextInt(anyInt())).thenReturn(0);
+
+ DiceResult result = diceService.play(user, diceDto);
+
+ assertFalse(result.isWin());
+ assertEquals(BigDecimal.valueOf(1), result.getRolledValue());
+ assertEquals(BigDecimal.ZERO, result.getPayout());
+ }
+
+ @Test
+ void play_rollUnder_targetValueOne_win() {
+ diceDto.setRollOver(false);
+ diceDto.setTargetValue(BigDecimal.valueOf(1));
+ when(random.nextInt(anyInt())).thenReturn(0);
+
+ DiceResult result = diceService.play(user, diceDto);
+
+ assertFalse(result.isWin());
+ assertEquals(BigDecimal.valueOf(1), result.getRolledValue());
+ assertEquals(BigDecimal.ZERO, result.getPayout());
+ }
+
+ @Test
+ void play_rollOver_targetValueNinetyNine_win() {
+ diceDto.setRollOver(true);
+ diceDto.setTargetValue(BigDecimal.valueOf(99));
+ when(random.nextInt(anyInt())).thenReturn(99);
+
+ DiceResult result = diceService.play(user, diceDto);
+
+ assertTrue(result.isWin());
+ assertEquals(BigDecimal.valueOf(100), result.getRolledValue());
+ }
+
+ @Test
+ void play_rollUnder_targetValueNinetyNine_win() {
+ diceDto.setRollOver(false);
+ diceDto.setTargetValue(BigDecimal.valueOf(99));
+ when(random.nextInt(anyInt())).thenReturn(97);
+
+ DiceResult result = diceService.play(user, diceDto);
+
+ assertTrue(result.isWin());
+ assertEquals(BigDecimal.valueOf(98), result.getRolledValue());
+ }
+
+ @Test
+ void play_rollOver_targetValueOneHundred_lose() {
+ diceDto.setRollOver(true);
+ diceDto.setTargetValue(BigDecimal.valueOf(100));
+ when(random.nextInt(anyInt())).thenReturn(99);
+
+ DiceResult result = diceService.play(user, diceDto);
+
+ assertFalse(result.isWin());
+ assertEquals(BigDecimal.valueOf(100), result.getRolledValue());
+ assertEquals(BigDecimal.ZERO, result.getPayout());
+ }
+
+ @Test
+ void play_rollUnder_targetValueOneHundred_win() {
+ diceDto.setRollOver(false);
+ diceDto.setTargetValue(BigDecimal.valueOf(100));
+ when(random.nextInt(anyInt())).thenReturn(98);
+
+ DiceResult result = diceService.play(user, diceDto);
+
+ assertTrue(result.isWin());
+ assertEquals(BigDecimal.valueOf(99), result.getRolledValue());
+ }
+
+ @Test
+ void play_payoutCalculationCorrect() {
+ diceDto.setRollOver(true);
+ diceDto.setTargetValue(BigDecimal.valueOf(75));
+ when(random.nextInt(anyInt())).thenReturn(75);
+
+ // Multiplier for win chance 25: (100-1)/25 = 99/25 = 3.96
+ // Payout: 10 * 3.96 = 39.6
+
+ DiceResult result = diceService.play(user, diceDto);
+
+ assertTrue(result.isWin());
+ assertEquals(BigDecimal.valueOf(39.6).stripTrailingZeros(), result.getPayout().stripTrailingZeros());
+ }
+
+ @Test
+ void play_payoutCalculationCorrect_rollUnder() {
+ diceDto.setRollOver(false);
+ diceDto.setTargetValue(BigDecimal.valueOf(25));
+ when(random.nextInt(anyInt())).thenReturn(0);
+
+ // Multiplier for win chance 24: (100-1)/24 = 99/24 = 4.125
+ // Payout: 10 * 4.125 = 41.25
+
+ DiceResult result = diceService.play(user, diceDto);
+
+ assertTrue(result.isWin());
+ assertEquals(BigDecimal.valueOf(41.25).stripTrailingZeros(), result.getPayout().stripTrailingZeros());
+ }
+
+ @Test
+ void play_betAmountSubtracted() {
+ when(random.nextInt(anyInt())).thenReturn(50);
+
+ diceService.play(user, diceDto);
+
+ verify(balanceService, times(1)).subtractFunds(user, diceDto.getBetAmount());
+ }
+}
From 78b8f4696c658d15c4b388577dce59fc5a85d7be Mon Sep 17 00:00:00 2001
From: Phan Huy Tran
Date: Wed, 28 May 2025 12:58:40 +0200
Subject: [PATCH 2/7] test: refactor
---
.../de/szut/casino/dice/DiceServiceTest.java | 61 +++++++++++++++----
1 file changed, 48 insertions(+), 13 deletions(-)
diff --git a/backend/src/test/java/de/szut/casino/dice/DiceServiceTest.java b/backend/src/test/java/de/szut/casino/dice/DiceServiceTest.java
index 21ca6cf..ec5f979 100644
--- a/backend/src/test/java/de/szut/casino/dice/DiceServiceTest.java
+++ b/backend/src/test/java/de/szut/casino/dice/DiceServiceTest.java
@@ -10,6 +10,7 @@ import org.mockito.Mock;
import org.mockito.junit.jupiter.MockitoExtension;
import java.math.BigDecimal;
+import java.math.RoundingMode;
import java.util.Random;
import static org.junit.jupiter.api.Assertions.*;
@@ -101,78 +102,112 @@ class DiceServiceTest {
}
@Test
- void play_rollOver_targetValueOne_lose() {
+ void play_rollOver_targetValueOne_rolledOne_lose() {
diceDto.setRollOver(true);
diceDto.setTargetValue(BigDecimal.valueOf(1));
- when(random.nextInt(anyInt())).thenReturn(0);
+ when(random.nextInt(anyInt())).thenReturn(0); // rolledValue = 1
DiceResult result = diceService.play(user, diceDto);
assertFalse(result.isWin());
assertEquals(BigDecimal.valueOf(1), result.getRolledValue());
assertEquals(BigDecimal.ZERO, result.getPayout());
+ verify(balanceService, times(1)).subtractFunds(user, diceDto.getBetAmount());
+ verify(balanceService, never()).addFunds(eq(user), any(BigDecimal.class));
}
@Test
- void play_rollUnder_targetValueOne_win() {
+ void play_rollOver_targetValueOne_rolledTwo_win() {
+ diceDto.setRollOver(true);
+ diceDto.setTargetValue(BigDecimal.valueOf(1));
+ when(random.nextInt(anyInt())).thenReturn(1); // rolledValue = 2
+
+ DiceResult result = diceService.play(user, diceDto);
+
+ assertTrue(result.isWin());
+ assertEquals(BigDecimal.valueOf(2), result.getRolledValue());
+ // Win chance for target 1 (roll over) is 99. Multiplier = (100-1)/99 = 1
+ assertEquals(diceDto.getBetAmount().stripTrailingZeros(), result.getPayout().stripTrailingZeros());
+ verify(balanceService, times(1)).subtractFunds(user, diceDto.getBetAmount());
+ verify(balanceService, times(1)).addFunds(eq(user), any(BigDecimal.class));
+ }
+
+ @Test
+ void play_rollUnder_targetValueOne_alwaysLose_winChanceZero() {
diceDto.setRollOver(false);
diceDto.setTargetValue(BigDecimal.valueOf(1));
- when(random.nextInt(anyInt())).thenReturn(0);
+ when(random.nextInt(anyInt())).thenReturn(0); // rolledValue = 1
DiceResult result = diceService.play(user, diceDto);
assertFalse(result.isWin());
assertEquals(BigDecimal.valueOf(1), result.getRolledValue());
assertEquals(BigDecimal.ZERO, result.getPayout());
+ verify(balanceService, times(1)).subtractFunds(user, diceDto.getBetAmount());
+ verify(balanceService, never()).addFunds(eq(user), any(BigDecimal.class));
}
@Test
- void play_rollOver_targetValueNinetyNine_win() {
+ void play_rollOver_targetValueNinetyNine_rolledHundred_win() {
diceDto.setRollOver(true);
diceDto.setTargetValue(BigDecimal.valueOf(99));
- when(random.nextInt(anyInt())).thenReturn(99);
+ when(random.nextInt(anyInt())).thenReturn(99); // rolledValue = 100
DiceResult result = diceService.play(user, diceDto);
assertTrue(result.isWin());
assertEquals(BigDecimal.valueOf(100), result.getRolledValue());
+ // Win chance for target 99 (roll over) is 1. Multiplier = (100-1)/1 = 99
+ assertEquals(diceDto.getBetAmount().multiply(BigDecimal.valueOf(99)).stripTrailingZeros(), result.getPayout().stripTrailingZeros());
+ verify(balanceService, times(1)).subtractFunds(user, diceDto.getBetAmount());
+ verify(balanceService, times(1)).addFunds(eq(user), any(BigDecimal.class));
}
@Test
- void play_rollUnder_targetValueNinetyNine_win() {
+ void play_rollUnder_targetValueNinetyNine_rolledNinetyEight_win() {
diceDto.setRollOver(false);
diceDto.setTargetValue(BigDecimal.valueOf(99));
- when(random.nextInt(anyInt())).thenReturn(97);
+ when(random.nextInt(anyInt())).thenReturn(97); // rolledValue = 98
DiceResult result = diceService.play(user, diceDto);
assertTrue(result.isWin());
assertEquals(BigDecimal.valueOf(98), result.getRolledValue());
+ // Win chance for target 99 (roll under) is 98. Multiplier = (100-1)/98 = 99/98
+ assertEquals(diceDto.getBetAmount().multiply(BigDecimal.valueOf(99).divide(BigDecimal.valueOf(98), 4, RoundingMode.HALF_UP)), result.getPayout());
+ verify(balanceService, times(1)).subtractFunds(user, diceDto.getBetAmount());
+ verify(balanceService, times(1)).addFunds(eq(user), any(BigDecimal.class));
}
@Test
- void play_rollOver_targetValueOneHundred_lose() {
+ void play_rollOver_targetValueOneHundred_alwaysLose_winChanceZero() {
diceDto.setRollOver(true);
diceDto.setTargetValue(BigDecimal.valueOf(100));
- when(random.nextInt(anyInt())).thenReturn(99);
+ when(random.nextInt(anyInt())).thenReturn(99); // rolledValue = 100
DiceResult result = diceService.play(user, diceDto);
assertFalse(result.isWin());
assertEquals(BigDecimal.valueOf(100), result.getRolledValue());
assertEquals(BigDecimal.ZERO, result.getPayout());
+ verify(balanceService, times(1)).subtractFunds(user, diceDto.getBetAmount());
+ verify(balanceService, never()).addFunds(eq(user), any(BigDecimal.class));
}
@Test
- void play_rollUnder_targetValueOneHundred_win() {
- diceDto.setRollOver(false);
+ void play_rollUnder_targetValueOneHundred_rolledNinetyNine_win() {
+ diceDto.setRollOver(false); // Assuming a typo in original, should be setRollOver(false)
diceDto.setTargetValue(BigDecimal.valueOf(100));
- when(random.nextInt(anyInt())).thenReturn(98);
+ when(random.nextInt(anyInt())).thenReturn(98); // rolledValue = 99
DiceResult result = diceService.play(user, diceDto);
assertTrue(result.isWin());
assertEquals(BigDecimal.valueOf(99), result.getRolledValue());
+ // Win chance for target 100 (roll under) is 99. Multiplier = (100-1)/99 = 1
+ assertEquals(diceDto.getBetAmount().stripTrailingZeros(), result.getPayout().stripTrailingZeros());
+ verify(balanceService, times(1)).subtractFunds(user, diceDto.getBetAmount());
+ verify(balanceService, times(1)).addFunds(eq(user), any(BigDecimal.class));
}
@Test
From 3e1c15e023aac8d8c97176bb5cb6618e5f3e4233 Mon Sep 17 00:00:00 2001
From: Phan Huy Tran
Date: Wed, 28 May 2025 13:01:18 +0200
Subject: [PATCH 3/7] test: adjust commments
---
.../de/szut/casino/dice/DiceServiceTest.java | 16 ++++++++--------
1 file changed, 8 insertions(+), 8 deletions(-)
diff --git a/backend/src/test/java/de/szut/casino/dice/DiceServiceTest.java b/backend/src/test/java/de/szut/casino/dice/DiceServiceTest.java
index ec5f979..6b2e230 100644
--- a/backend/src/test/java/de/szut/casino/dice/DiceServiceTest.java
+++ b/backend/src/test/java/de/szut/casino/dice/DiceServiceTest.java
@@ -105,7 +105,7 @@ class DiceServiceTest {
void play_rollOver_targetValueOne_rolledOne_lose() {
diceDto.setRollOver(true);
diceDto.setTargetValue(BigDecimal.valueOf(1));
- when(random.nextInt(anyInt())).thenReturn(0); // rolledValue = 1
+ when(random.nextInt(anyInt())).thenReturn(0);
DiceResult result = diceService.play(user, diceDto);
@@ -120,7 +120,7 @@ class DiceServiceTest {
void play_rollOver_targetValueOne_rolledTwo_win() {
diceDto.setRollOver(true);
diceDto.setTargetValue(BigDecimal.valueOf(1));
- when(random.nextInt(anyInt())).thenReturn(1); // rolledValue = 2
+ when(random.nextInt(anyInt())).thenReturn(1);
DiceResult result = diceService.play(user, diceDto);
@@ -136,7 +136,7 @@ class DiceServiceTest {
void play_rollUnder_targetValueOne_alwaysLose_winChanceZero() {
diceDto.setRollOver(false);
diceDto.setTargetValue(BigDecimal.valueOf(1));
- when(random.nextInt(anyInt())).thenReturn(0); // rolledValue = 1
+ when(random.nextInt(anyInt())).thenReturn(0);
DiceResult result = diceService.play(user, diceDto);
@@ -151,7 +151,7 @@ class DiceServiceTest {
void play_rollOver_targetValueNinetyNine_rolledHundred_win() {
diceDto.setRollOver(true);
diceDto.setTargetValue(BigDecimal.valueOf(99));
- when(random.nextInt(anyInt())).thenReturn(99); // rolledValue = 100
+ when(random.nextInt(anyInt())).thenReturn(99);
DiceResult result = diceService.play(user, diceDto);
@@ -167,7 +167,7 @@ class DiceServiceTest {
void play_rollUnder_targetValueNinetyNine_rolledNinetyEight_win() {
diceDto.setRollOver(false);
diceDto.setTargetValue(BigDecimal.valueOf(99));
- when(random.nextInt(anyInt())).thenReturn(97); // rolledValue = 98
+ when(random.nextInt(anyInt())).thenReturn(97);
DiceResult result = diceService.play(user, diceDto);
@@ -183,7 +183,7 @@ class DiceServiceTest {
void play_rollOver_targetValueOneHundred_alwaysLose_winChanceZero() {
diceDto.setRollOver(true);
diceDto.setTargetValue(BigDecimal.valueOf(100));
- when(random.nextInt(anyInt())).thenReturn(99); // rolledValue = 100
+ when(random.nextInt(anyInt())).thenReturn(99);
DiceResult result = diceService.play(user, diceDto);
@@ -196,9 +196,9 @@ class DiceServiceTest {
@Test
void play_rollUnder_targetValueOneHundred_rolledNinetyNine_win() {
- diceDto.setRollOver(false); // Assuming a typo in original, should be setRollOver(false)
+ diceDto.setRollOver(false);
diceDto.setTargetValue(BigDecimal.valueOf(100));
- when(random.nextInt(anyInt())).thenReturn(98); // rolledValue = 99
+ when(random.nextInt(anyInt())).thenReturn(98);
DiceResult result = diceService.play(user, diceDto);
From b061e14b53193a89a42037f6852a0c1aef9a0fa0 Mon Sep 17 00:00:00 2001
From: Renovate Bot
Date: Sun, 1 Jun 2025 12:02:21 +0000
Subject: [PATCH 4/7] fix(deps): update dependencies (major and minor)
---
frontend/bun.lock | 42 ++++++++++++++++++++----------------------
frontend/package.json | 20 ++++++++++----------
2 files changed, 30 insertions(+), 32 deletions(-)
diff --git a/frontend/bun.lock b/frontend/bun.lock
index c0b164d..d8179f4 100644
--- a/frontend/bun.lock
+++ b/frontend/bun.lock
@@ -4,16 +4,16 @@
"": {
"name": "lf10-starter2024",
"dependencies": {
- "@angular/animations": "^19.0.0",
- "@angular/cdk": "~19.2.0",
- "@angular/common": "^19.0.0",
- "@angular/compiler": "^19.2.4",
- "@angular/core": "^19.0.0",
- "@angular/forms": "^19.0.0",
- "@angular/platform-browser": "^19.0.0",
- "@angular/platform-browser-dynamic": "^19.0.0",
- "@angular/router": "^19.0.0",
- "@fortawesome/angular-fontawesome": "^1.0.0",
+ "@angular/animations": "^20.0.0",
+ "@angular/cdk": "~20.0.0",
+ "@angular/common": "^20.0.0",
+ "@angular/compiler": "^20.0.0",
+ "@angular/core": "^20.0.0",
+ "@angular/forms": "^20.0.0",
+ "@angular/platform-browser": "^20.0.0",
+ "@angular/platform-browser-dynamic": "^20.0.0",
+ "@angular/router": "^20.0.0",
+ "@fortawesome/angular-fontawesome": "^2.0.0",
"@fortawesome/fontawesome-svg-core": "^6.7.2",
"@fortawesome/free-brands-svg-icons": "^6.7.2",
"@fortawesome/free-solid-svg-icons": "^6.7.2",
@@ -76,29 +76,29 @@
"@angular-eslint/utils": ["@angular-eslint/utils@19.6.0", "", { "dependencies": { "@angular-eslint/bundled-angular-compiler": "19.6.0" }, "peerDependencies": { "@typescript-eslint/utils": "^7.11.0 || ^8.0.0", "eslint": "^8.57.0 || ^9.0.0", "typescript": "*" } }, "sha512-ygtsmRKHNqrzG2mpUj1XwLNRoG+ikYkizsOuq5xPRM8o6dCw03H5eel4s7hnXT4c09WbpnoaVNi9O3xFLIETJQ=="],
- "@angular/animations": ["@angular/animations@19.2.5", "", { "dependencies": { "tslib": "^2.3.0" }, "peerDependencies": { "@angular/common": "19.2.5", "@angular/core": "19.2.5" } }, "sha512-m4RtY3z1JuHFCh6OrOHxo25oKEigBDdR/XmdCfXIwfTiObZzNA7VQhysgdrb9IISO99kXbjZUYKDtLzgWT8Klg=="],
+ "@angular/animations": ["@angular/animations@20.0.0", "", { "dependencies": { "tslib": "^2.3.0" }, "peerDependencies": { "@angular/common": "20.0.0", "@angular/core": "20.0.0" } }, "sha512-yU4hUH6AheY0dnMSaLRMfgnXhg/JUSUvrhE+lHzIiSKdEf0lyo1Ri6bkPD1CbamxZ94BqhRNCApvbvTbibGICQ=="],
"@angular/build": ["@angular/build@19.2.6", "", { "dependencies": { "@ampproject/remapping": "2.3.0", "@angular-devkit/architect": "0.1902.6", "@babel/core": "7.26.10", "@babel/helper-annotate-as-pure": "7.25.9", "@babel/helper-split-export-declaration": "7.24.7", "@babel/plugin-syntax-import-attributes": "7.26.0", "@inquirer/confirm": "5.1.6", "@vitejs/plugin-basic-ssl": "1.2.0", "beasties": "0.2.0", "browserslist": "^4.23.0", "esbuild": "0.25.1", "fast-glob": "3.3.3", "https-proxy-agent": "7.0.6", "istanbul-lib-instrument": "6.0.3", "listr2": "8.2.5", "magic-string": "0.30.17", "mrmime": "2.0.1", "parse5-html-rewriting-stream": "7.0.0", "picomatch": "4.0.2", "piscina": "4.8.0", "rollup": "4.34.8", "sass": "1.85.0", "semver": "7.7.1", "source-map-support": "0.5.21", "vite": "6.2.4", "watchpack": "2.4.2" }, "optionalDependencies": { "lmdb": "3.2.6" }, "peerDependencies": { "@angular/compiler": "^19.0.0 || ^19.2.0-next.0", "@angular/compiler-cli": "^19.0.0 || ^19.2.0-next.0", "@angular/localize": "^19.0.0 || ^19.2.0-next.0", "@angular/platform-server": "^19.0.0 || ^19.2.0-next.0", "@angular/service-worker": "^19.0.0 || ^19.2.0-next.0", "@angular/ssr": "^19.2.6", "karma": "^6.4.0", "less": "^4.2.0", "ng-packagr": "^19.0.0 || ^19.2.0-next.0", "postcss": "^8.4.0", "tailwindcss": "^2.0.0 || ^3.0.0 || ^4.0.0", "typescript": ">=5.5 <5.9" }, "optionalPeers": ["@angular/localize", "@angular/platform-server", "@angular/service-worker", "@angular/ssr", "karma", "less", "ng-packagr", "postcss", "tailwindcss"] }, "sha512-+VBLb4ZPLswwJmgfsTFzGex+Sq/WveNc+uaIWyHYjwnuI17NXe1qAAg1rlp72CqGn0cirisfOyAUwPc/xZAgTg=="],
- "@angular/cdk": ["@angular/cdk@19.2.8", "", { "dependencies": { "parse5": "^7.1.2", "tslib": "^2.3.0" }, "peerDependencies": { "@angular/common": "^19.0.0 || ^20.0.0", "@angular/core": "^19.0.0 || ^20.0.0", "rxjs": "^6.5.3 || ^7.4.0" } }, "sha512-ZZqWVYFF80TdjWkk2sc9Pn2luhiYeC78VH3Yjeln4wXMsTGDsvKPBcuOxSxxpJ31saaVBehDjBUuXMqGRj8KuA=="],
+ "@angular/cdk": ["@angular/cdk@20.0.1", "", { "dependencies": { "parse5": "^7.1.2", "tslib": "^2.3.0" }, "peerDependencies": { "@angular/common": "^20.0.0 || ^21.0.0", "@angular/core": "^20.0.0 || ^21.0.0", "rxjs": "^6.5.3 || ^7.4.0" } }, "sha512-llJIyKdF9D0hJ9/PNy9A5vmayNgHr7MtQrtjpeLyPuK8qkUnxQd9Hzv5olqixRrbxxDs/Lt0l1T2ViHGy7WYhg=="],
"@angular/cli": ["@angular/cli@19.2.6", "", { "dependencies": { "@angular-devkit/architect": "0.1902.6", "@angular-devkit/core": "19.2.6", "@angular-devkit/schematics": "19.2.6", "@inquirer/prompts": "7.3.2", "@listr2/prompt-adapter-inquirer": "2.0.18", "@schematics/angular": "19.2.6", "@yarnpkg/lockfile": "1.1.0", "ini": "5.0.0", "jsonc-parser": "3.3.1", "listr2": "8.2.5", "npm-package-arg": "12.0.2", "npm-pick-manifest": "10.0.0", "pacote": "20.0.0", "resolve": "1.22.10", "semver": "7.7.1", "symbol-observable": "4.0.0", "yargs": "17.7.2" }, "bin": { "ng": "bin/ng.js" } }, "sha512-eZhFOSsDUHKaciwcWdU5C54ViAvPPdZJf42So93G2vZWDtEq6Uk47huocn1FY9cMhDvURfYLNrrLMpUDtUSsSA=="],
- "@angular/common": ["@angular/common@19.2.5", "", { "dependencies": { "tslib": "^2.3.0" }, "peerDependencies": { "@angular/core": "19.2.5", "rxjs": "^6.5.3 || ^7.4.0" } }, "sha512-vFCBdas4C5PxP6ts/4TlRddWD3DUmI3aaO0QZdZvqyLHy428t84ruYdsJXKaeD8ie2U4/9F3a1tsklclRG/BBA=="],
+ "@angular/common": ["@angular/common@20.0.0", "", { "dependencies": { "tslib": "^2.3.0" }, "peerDependencies": { "@angular/core": "20.0.0", "rxjs": "^6.5.3 || ^7.4.0" } }, "sha512-tZTvxDjx+wH74/hIpip63u4tlaXNVXkq1iVf4gk7RPQGCAYLNPDWma8X+RpXMXWikn4/mA5NS1VBBtStTbS+gg=="],
- "@angular/compiler": ["@angular/compiler@19.2.5", "", { "dependencies": { "tslib": "^2.3.0" } }, "sha512-34J+HubQjwkbZ0AUtU5sa4Zouws9XtP/fKaysMQecoYJTZ3jewzLSRu3aAEZX1Y4gIrcVVKKIxM6oWoXKwYMOA=="],
+ "@angular/compiler": ["@angular/compiler@20.0.0", "", { "dependencies": { "tslib": "^2.3.0" } }, "sha512-RzS7MFNy/f8Tft0u6Q1zszzFTeki4408zsBALwmS91a8O8x/jaEvfwA7swC7RiqiX9KKmAyuBJ0qiv42v1T5dA=="],
"@angular/compiler-cli": ["@angular/compiler-cli@19.2.5", "", { "dependencies": { "@babel/core": "7.26.9", "@jridgewell/sourcemap-codec": "^1.4.14", "chokidar": "^4.0.0", "convert-source-map": "^1.5.1", "reflect-metadata": "^0.2.0", "semver": "^7.0.0", "tslib": "^2.3.0", "yargs": "^17.2.1" }, "peerDependencies": { "@angular/compiler": "19.2.5", "typescript": ">=5.5 <5.9" }, "bin": { "ngc": "bundles/src/bin/ngc.js", "ngcc": "bundles/ngcc/index.js", "ng-xi18n": "bundles/src/bin/ng_xi18n.js" } }, "sha512-b2cG41r6lilApXLlvja1Ra2D00dM3BxmQhoElKC1tOnpD6S3/krlH1DOnBB2I55RBn9iv4zdmPz1l8zPUSh7DQ=="],
- "@angular/core": ["@angular/core@19.2.5", "", { "dependencies": { "tslib": "^2.3.0" }, "peerDependencies": { "rxjs": "^6.5.3 || ^7.4.0", "zone.js": "~0.15.0" } }, "sha512-NNEz1sEZz1mBpgf6Tz3aJ9b8KjqpTiMYhHfCYA9h9Ipe4D8gUmOsvPHPK2M755OX7p7PmUmzp1XCUHYrZMVHRw=="],
+ "@angular/core": ["@angular/core@20.0.0", "", { "dependencies": { "tslib": "^2.3.0" }, "peerDependencies": { "@angular/compiler": "20.0.0", "rxjs": "^6.5.3 || ^7.4.0", "zone.js": "~0.15.0" }, "optionalPeers": ["@angular/compiler", "zone.js"] }, "sha512-2UjKbTtYSY8omY+LE4G6hQ1/R4PkE6NY7/2u99TxLH/oOnc9broCH1g9ITU+n0eJURcOFeK0/w6RdSrK+di3pg=="],
- "@angular/forms": ["@angular/forms@19.2.5", "", { "dependencies": { "tslib": "^2.3.0" }, "peerDependencies": { "@angular/common": "19.2.5", "@angular/core": "19.2.5", "@angular/platform-browser": "19.2.5", "rxjs": "^6.5.3 || ^7.4.0" } }, "sha512-2Zvy3qK1kOxiAX9fdSaeG48q7oyO/4RlMYlg1w+ra9qX1SrgwF3OQ2P2Vs+ojg1AxN3z9xFp4aYaaID/G2LZAw=="],
+ "@angular/forms": ["@angular/forms@20.0.0", "", { "dependencies": { "tslib": "^2.3.0" }, "peerDependencies": { "@angular/common": "20.0.0", "@angular/core": "20.0.0", "@angular/platform-browser": "20.0.0", "rxjs": "^6.5.3 || ^7.4.0" } }, "sha512-6yeb99IrNyeyj7o0bbd+n3JTZrXX2dJfdYLJH3tlXVlO9wg63bq+YR1AeM+RDCYMs+YDJis0lQpF6s+OICJv4g=="],
- "@angular/platform-browser": ["@angular/platform-browser@19.2.5", "", { "dependencies": { "tslib": "^2.3.0" }, "peerDependencies": { "@angular/animations": "19.2.5", "@angular/common": "19.2.5", "@angular/core": "19.2.5" }, "optionalPeers": ["@angular/animations"] }, "sha512-Lshy++X16cvl6OPvfzMySpsqEaCPKEJmDjz7q7oSt96oxlh6LvOeOUVLjsNyrNaIt9NadpWoqjlu/I9RTPJkpw=="],
+ "@angular/platform-browser": ["@angular/platform-browser@20.0.0", "", { "dependencies": { "tslib": "^2.3.0" }, "peerDependencies": { "@angular/animations": "20.0.0", "@angular/common": "20.0.0", "@angular/core": "20.0.0" }, "optionalPeers": ["@angular/animations"] }, "sha512-FP9YjT2beF0tov0wub6+eUQqJd2MwyYqEQQ6+Qx67ukd04plIryhrcImORehrsN24DbnHkyTqhCvUyNAZs2uwA=="],
- "@angular/platform-browser-dynamic": ["@angular/platform-browser-dynamic@19.2.5", "", { "dependencies": { "tslib": "^2.3.0" }, "peerDependencies": { "@angular/common": "19.2.5", "@angular/compiler": "19.2.5", "@angular/core": "19.2.5", "@angular/platform-browser": "19.2.5" } }, "sha512-15in8u4552EcdWNTXY2h0MKuJbk3AuXwWr0zVTum4CfB/Ss2tNTrDEdWhgAbhnUI0e9jZQee/fhBbA1rleMYrA=="],
+ "@angular/platform-browser-dynamic": ["@angular/platform-browser-dynamic@20.0.0", "", { "dependencies": { "tslib": "^2.3.0" }, "peerDependencies": { "@angular/common": "20.0.0", "@angular/compiler": "20.0.0", "@angular/core": "20.0.0", "@angular/platform-browser": "20.0.0" } }, "sha512-AACq3Ijuq59SdLDmfxWU8hYlo8O4Br9OHWNAga2W0X6p/7HlpeZZVdTlb/KGVYRKJvGpgSB10QYlRPfm215q9Q=="],
- "@angular/router": ["@angular/router@19.2.5", "", { "dependencies": { "tslib": "^2.3.0" }, "peerDependencies": { "@angular/common": "19.2.5", "@angular/core": "19.2.5", "@angular/platform-browser": "19.2.5", "rxjs": "^6.5.3 || ^7.4.0" } }, "sha512-9pSfmdNXLjaOKj0kd4UxBC7sFdCFOnRGbftp397G3KWqsLsGSKmNFzqhXNeA5QHkaVxnpmpm8HzXU+zYV5JwSg=="],
+ "@angular/router": ["@angular/router@20.0.0", "", { "dependencies": { "tslib": "^2.3.0" }, "peerDependencies": { "@angular/common": "20.0.0", "@angular/core": "20.0.0", "@angular/platform-browser": "20.0.0", "rxjs": "^6.5.3 || ^7.4.0" } }, "sha512-RQ7rU4NaZDSvvOfMZQmB50q7de+jrHYb+f0ExLKBvr80B1MK3oc9VvI2BzBkGfM4aGx71MMa0UizjOiT/31kqw=="],
"@babel/code-frame": ["@babel/code-frame@7.26.2", "", { "dependencies": { "@babel/helper-validator-identifier": "^7.25.9", "js-tokens": "^4.0.0", "picocolors": "^1.0.0" } }, "sha512-RJlIHRueQgwWitWgF8OdFYGZX328Ax5BCemNGlqHfplnRT9ESi8JkFlvaVYbS+UubVY6dpv87Fs2u5M29iNFVQ=="],
@@ -352,7 +352,7 @@
"@eslint/plugin-kit": ["@eslint/plugin-kit@0.2.8", "", { "dependencies": { "@eslint/core": "^0.13.0", "levn": "^0.4.1" } }, "sha512-ZAoA40rNMPwSm+AeHpCq8STiNAwzWLJuP8Xv4CHIc9wv/PSuExjMrmjfYNj682vW0OOiZ1HKxzvjQr9XZIisQA=="],
- "@fortawesome/angular-fontawesome": ["@fortawesome/angular-fontawesome@1.0.0", "", { "dependencies": { "@fortawesome/fontawesome-svg-core": "^6.7.1", "tslib": "^2.8.1" }, "peerDependencies": { "@angular/core": "^19.0.0" } }, "sha512-EC2fYuXIuw2ld1kzJi+zysWus6OeGGfLQtbh0hW9zyyq5aBo8ZJkcJKBsVQ8E6Mg7nHyTWaXn+sdcXTPDWz+UQ=="],
+ "@fortawesome/angular-fontawesome": ["@fortawesome/angular-fontawesome@2.0.0", "", { "dependencies": { "@fortawesome/fontawesome-svg-core": "^6.7.2", "tslib": "^2.8.1" }, "peerDependencies": { "@angular/core": "^20.0.0" } }, "sha512-xsPfjcs7elZYWo1Q19IrqMd5GdUAyPGnwElq7x/x9PKIwYXsnCaEtJkMUkZUj3Yesdkmqs0X0IMOwZvz7BNAtw=="],
"@fortawesome/fontawesome-common-types": ["@fortawesome/fontawesome-common-types@6.7.2", "", {}, "sha512-Zs+YeHUC5fkt7Mg1l6XTniei3k4bwG/yo3iFUtZWd/pMx9g3fdvkSK9E0FOC+++phXOka78uJcYb8JaFkW52Xg=="],
@@ -1868,8 +1868,6 @@
"yoctocolors-cjs": ["yoctocolors-cjs@2.1.2", "", {}, "sha512-cYVsTjKl8b+FrnidjibDWskAv7UKOfcwaVZdp/it9n1s9fU3IkgDbhdIRKCW4JDsAlECJY0ytoVPT3sK6kideA=="],
- "zone.js": ["zone.js@0.15.0", "", {}, "sha512-9oxn0IIjbCZkJ67L+LkhYWRyAy7axphb3VgE2MBDlOqnmHMPWGYMxJxBYFueFq/JGY2GMwS0rU+UCLunEmy5UA=="],
-
"@angular-devkit/architect/rxjs": ["rxjs@7.8.1", "", { "dependencies": { "tslib": "^2.1.0" } }, "sha512-AA3TVj+0A2iuIoQkWEK/tqFjBq2j+6PO6Y0zJcvzLAFhEFIO3HL0vls9hWLncZbAAbK0mar7oZ4V079I/qPMxg=="],
"@angular-devkit/build-angular/postcss": ["postcss@8.5.2", "", { "dependencies": { "nanoid": "^3.3.8", "picocolors": "^1.1.1", "source-map-js": "^1.2.1" } }, "sha512-MjOadfU3Ys9KYoX0AdkBlFEF1Vx37uCCeN4ZHnmwm9FfpbsGWMZeBLMmmpY+6Ocqod7mkdZ0DT31OlbsFrLlkA=="],
diff --git a/frontend/package.json b/frontend/package.json
index d5460c5..5de77bb 100644
--- a/frontend/package.json
+++ b/frontend/package.json
@@ -14,16 +14,16 @@
},
"private": true,
"dependencies": {
- "@angular/animations": "^19.0.0",
- "@angular/cdk": "~19.2.0",
- "@angular/common": "^19.0.0",
- "@angular/compiler": "^19.2.4",
- "@angular/core": "^19.0.0",
- "@angular/forms": "^19.0.0",
- "@angular/platform-browser": "^19.0.0",
- "@angular/platform-browser-dynamic": "^19.0.0",
- "@angular/router": "^19.0.0",
- "@fortawesome/angular-fontawesome": "^1.0.0",
+ "@angular/animations": "^20.0.0",
+ "@angular/cdk": "~20.0.0",
+ "@angular/common": "^20.0.0",
+ "@angular/compiler": "^20.0.0",
+ "@angular/core": "^20.0.0",
+ "@angular/forms": "^20.0.0",
+ "@angular/platform-browser": "^20.0.0",
+ "@angular/platform-browser-dynamic": "^20.0.0",
+ "@angular/router": "^20.0.0",
+ "@fortawesome/angular-fontawesome": "^2.0.0",
"@fortawesome/fontawesome-svg-core": "^6.7.2",
"@fortawesome/free-brands-svg-icons": "^6.7.2",
"@fortawesome/free-solid-svg-icons": "^6.7.2",
From bd1d8f83393c8c0f36c068be344ca6d56fd0e07b Mon Sep 17 00:00:00 2001
From: Jan K9f
Date: Mon, 2 Jun 2025 14:04:15 +0200
Subject: [PATCH 5/7] chore: Add stale pipeline
---
.gitea/workflows/stale.yml | 14 ++++++++++++++
1 file changed, 14 insertions(+)
create mode 100644 .gitea/workflows/stale.yml
diff --git a/.gitea/workflows/stale.yml b/.gitea/workflows/stale.yml
new file mode 100644
index 0000000..773eac4
--- /dev/null
+++ b/.gitea/workflows/stale.yml
@@ -0,0 +1,14 @@
+name: "Close stale issues and PRs"
+on:
+ schedule:
+ - cron: "30 1 * * *"
+
+jobs:
+ stale:
+ runs-on: ubuntu-latest
+ steps:
+ - uses: actions/stale@v9
+ with:
+ stale-pr-message: "Will be closed in x days bc yo mom is a bitch. im not telling you when it will be closed fuckface"
+ days-before-pr-stale: 2
+ days-before-pr-close: 3
From 9639525ddd35a4f9272dd98ac5e3e1a464b57fc4 Mon Sep 17 00:00:00 2001
From: Jan K9f
Date: Mon, 2 Jun 2025 14:31:55 +0200
Subject: [PATCH 6/7] fix: Hourly
---
.gitea/workflows/stale.yml | 3 ++-
1 file changed, 2 insertions(+), 1 deletion(-)
diff --git a/.gitea/workflows/stale.yml b/.gitea/workflows/stale.yml
index 773eac4..7ba5af9 100644
--- a/.gitea/workflows/stale.yml
+++ b/.gitea/workflows/stale.yml
@@ -1,7 +1,8 @@
name: "Close stale issues and PRs"
on:
+ workflow_dispatch:
schedule:
- - cron: "30 1 * * *"
+ - cron: "@hourly"
jobs:
stale:
From 1c75c5c5feb0d555318e30ea4288c348346263d3 Mon Sep 17 00:00:00 2001
From: Renovate Bot
Date: Mon, 2 Jun 2025 13:02:42 +0000
Subject: [PATCH 7/7] fix(deps): update dependencies (major and minor)
---
frontend/bun.lock | 42 ++++++++++++++++++++----------------------
frontend/package.json | 20 ++++++++++----------
2 files changed, 30 insertions(+), 32 deletions(-)
diff --git a/frontend/bun.lock b/frontend/bun.lock
index c0b164d..d8179f4 100644
--- a/frontend/bun.lock
+++ b/frontend/bun.lock
@@ -4,16 +4,16 @@
"": {
"name": "lf10-starter2024",
"dependencies": {
- "@angular/animations": "^19.0.0",
- "@angular/cdk": "~19.2.0",
- "@angular/common": "^19.0.0",
- "@angular/compiler": "^19.2.4",
- "@angular/core": "^19.0.0",
- "@angular/forms": "^19.0.0",
- "@angular/platform-browser": "^19.0.0",
- "@angular/platform-browser-dynamic": "^19.0.0",
- "@angular/router": "^19.0.0",
- "@fortawesome/angular-fontawesome": "^1.0.0",
+ "@angular/animations": "^20.0.0",
+ "@angular/cdk": "~20.0.0",
+ "@angular/common": "^20.0.0",
+ "@angular/compiler": "^20.0.0",
+ "@angular/core": "^20.0.0",
+ "@angular/forms": "^20.0.0",
+ "@angular/platform-browser": "^20.0.0",
+ "@angular/platform-browser-dynamic": "^20.0.0",
+ "@angular/router": "^20.0.0",
+ "@fortawesome/angular-fontawesome": "^2.0.0",
"@fortawesome/fontawesome-svg-core": "^6.7.2",
"@fortawesome/free-brands-svg-icons": "^6.7.2",
"@fortawesome/free-solid-svg-icons": "^6.7.2",
@@ -76,29 +76,29 @@
"@angular-eslint/utils": ["@angular-eslint/utils@19.6.0", "", { "dependencies": { "@angular-eslint/bundled-angular-compiler": "19.6.0" }, "peerDependencies": { "@typescript-eslint/utils": "^7.11.0 || ^8.0.0", "eslint": "^8.57.0 || ^9.0.0", "typescript": "*" } }, "sha512-ygtsmRKHNqrzG2mpUj1XwLNRoG+ikYkizsOuq5xPRM8o6dCw03H5eel4s7hnXT4c09WbpnoaVNi9O3xFLIETJQ=="],
- "@angular/animations": ["@angular/animations@19.2.5", "", { "dependencies": { "tslib": "^2.3.0" }, "peerDependencies": { "@angular/common": "19.2.5", "@angular/core": "19.2.5" } }, "sha512-m4RtY3z1JuHFCh6OrOHxo25oKEigBDdR/XmdCfXIwfTiObZzNA7VQhysgdrb9IISO99kXbjZUYKDtLzgWT8Klg=="],
+ "@angular/animations": ["@angular/animations@20.0.0", "", { "dependencies": { "tslib": "^2.3.0" }, "peerDependencies": { "@angular/common": "20.0.0", "@angular/core": "20.0.0" } }, "sha512-yU4hUH6AheY0dnMSaLRMfgnXhg/JUSUvrhE+lHzIiSKdEf0lyo1Ri6bkPD1CbamxZ94BqhRNCApvbvTbibGICQ=="],
"@angular/build": ["@angular/build@19.2.6", "", { "dependencies": { "@ampproject/remapping": "2.3.0", "@angular-devkit/architect": "0.1902.6", "@babel/core": "7.26.10", "@babel/helper-annotate-as-pure": "7.25.9", "@babel/helper-split-export-declaration": "7.24.7", "@babel/plugin-syntax-import-attributes": "7.26.0", "@inquirer/confirm": "5.1.6", "@vitejs/plugin-basic-ssl": "1.2.0", "beasties": "0.2.0", "browserslist": "^4.23.0", "esbuild": "0.25.1", "fast-glob": "3.3.3", "https-proxy-agent": "7.0.6", "istanbul-lib-instrument": "6.0.3", "listr2": "8.2.5", "magic-string": "0.30.17", "mrmime": "2.0.1", "parse5-html-rewriting-stream": "7.0.0", "picomatch": "4.0.2", "piscina": "4.8.0", "rollup": "4.34.8", "sass": "1.85.0", "semver": "7.7.1", "source-map-support": "0.5.21", "vite": "6.2.4", "watchpack": "2.4.2" }, "optionalDependencies": { "lmdb": "3.2.6" }, "peerDependencies": { "@angular/compiler": "^19.0.0 || ^19.2.0-next.0", "@angular/compiler-cli": "^19.0.0 || ^19.2.0-next.0", "@angular/localize": "^19.0.0 || ^19.2.0-next.0", "@angular/platform-server": "^19.0.0 || ^19.2.0-next.0", "@angular/service-worker": "^19.0.0 || ^19.2.0-next.0", "@angular/ssr": "^19.2.6", "karma": "^6.4.0", "less": "^4.2.0", "ng-packagr": "^19.0.0 || ^19.2.0-next.0", "postcss": "^8.4.0", "tailwindcss": "^2.0.0 || ^3.0.0 || ^4.0.0", "typescript": ">=5.5 <5.9" }, "optionalPeers": ["@angular/localize", "@angular/platform-server", "@angular/service-worker", "@angular/ssr", "karma", "less", "ng-packagr", "postcss", "tailwindcss"] }, "sha512-+VBLb4ZPLswwJmgfsTFzGex+Sq/WveNc+uaIWyHYjwnuI17NXe1qAAg1rlp72CqGn0cirisfOyAUwPc/xZAgTg=="],
- "@angular/cdk": ["@angular/cdk@19.2.8", "", { "dependencies": { "parse5": "^7.1.2", "tslib": "^2.3.0" }, "peerDependencies": { "@angular/common": "^19.0.0 || ^20.0.0", "@angular/core": "^19.0.0 || ^20.0.0", "rxjs": "^6.5.3 || ^7.4.0" } }, "sha512-ZZqWVYFF80TdjWkk2sc9Pn2luhiYeC78VH3Yjeln4wXMsTGDsvKPBcuOxSxxpJ31saaVBehDjBUuXMqGRj8KuA=="],
+ "@angular/cdk": ["@angular/cdk@20.0.1", "", { "dependencies": { "parse5": "^7.1.2", "tslib": "^2.3.0" }, "peerDependencies": { "@angular/common": "^20.0.0 || ^21.0.0", "@angular/core": "^20.0.0 || ^21.0.0", "rxjs": "^6.5.3 || ^7.4.0" } }, "sha512-llJIyKdF9D0hJ9/PNy9A5vmayNgHr7MtQrtjpeLyPuK8qkUnxQd9Hzv5olqixRrbxxDs/Lt0l1T2ViHGy7WYhg=="],
"@angular/cli": ["@angular/cli@19.2.6", "", { "dependencies": { "@angular-devkit/architect": "0.1902.6", "@angular-devkit/core": "19.2.6", "@angular-devkit/schematics": "19.2.6", "@inquirer/prompts": "7.3.2", "@listr2/prompt-adapter-inquirer": "2.0.18", "@schematics/angular": "19.2.6", "@yarnpkg/lockfile": "1.1.0", "ini": "5.0.0", "jsonc-parser": "3.3.1", "listr2": "8.2.5", "npm-package-arg": "12.0.2", "npm-pick-manifest": "10.0.0", "pacote": "20.0.0", "resolve": "1.22.10", "semver": "7.7.1", "symbol-observable": "4.0.0", "yargs": "17.7.2" }, "bin": { "ng": "bin/ng.js" } }, "sha512-eZhFOSsDUHKaciwcWdU5C54ViAvPPdZJf42So93G2vZWDtEq6Uk47huocn1FY9cMhDvURfYLNrrLMpUDtUSsSA=="],
- "@angular/common": ["@angular/common@19.2.5", "", { "dependencies": { "tslib": "^2.3.0" }, "peerDependencies": { "@angular/core": "19.2.5", "rxjs": "^6.5.3 || ^7.4.0" } }, "sha512-vFCBdas4C5PxP6ts/4TlRddWD3DUmI3aaO0QZdZvqyLHy428t84ruYdsJXKaeD8ie2U4/9F3a1tsklclRG/BBA=="],
+ "@angular/common": ["@angular/common@20.0.0", "", { "dependencies": { "tslib": "^2.3.0" }, "peerDependencies": { "@angular/core": "20.0.0", "rxjs": "^6.5.3 || ^7.4.0" } }, "sha512-tZTvxDjx+wH74/hIpip63u4tlaXNVXkq1iVf4gk7RPQGCAYLNPDWma8X+RpXMXWikn4/mA5NS1VBBtStTbS+gg=="],
- "@angular/compiler": ["@angular/compiler@19.2.5", "", { "dependencies": { "tslib": "^2.3.0" } }, "sha512-34J+HubQjwkbZ0AUtU5sa4Zouws9XtP/fKaysMQecoYJTZ3jewzLSRu3aAEZX1Y4gIrcVVKKIxM6oWoXKwYMOA=="],
+ "@angular/compiler": ["@angular/compiler@20.0.0", "", { "dependencies": { "tslib": "^2.3.0" } }, "sha512-RzS7MFNy/f8Tft0u6Q1zszzFTeki4408zsBALwmS91a8O8x/jaEvfwA7swC7RiqiX9KKmAyuBJ0qiv42v1T5dA=="],
"@angular/compiler-cli": ["@angular/compiler-cli@19.2.5", "", { "dependencies": { "@babel/core": "7.26.9", "@jridgewell/sourcemap-codec": "^1.4.14", "chokidar": "^4.0.0", "convert-source-map": "^1.5.1", "reflect-metadata": "^0.2.0", "semver": "^7.0.0", "tslib": "^2.3.0", "yargs": "^17.2.1" }, "peerDependencies": { "@angular/compiler": "19.2.5", "typescript": ">=5.5 <5.9" }, "bin": { "ngc": "bundles/src/bin/ngc.js", "ngcc": "bundles/ngcc/index.js", "ng-xi18n": "bundles/src/bin/ng_xi18n.js" } }, "sha512-b2cG41r6lilApXLlvja1Ra2D00dM3BxmQhoElKC1tOnpD6S3/krlH1DOnBB2I55RBn9iv4zdmPz1l8zPUSh7DQ=="],
- "@angular/core": ["@angular/core@19.2.5", "", { "dependencies": { "tslib": "^2.3.0" }, "peerDependencies": { "rxjs": "^6.5.3 || ^7.4.0", "zone.js": "~0.15.0" } }, "sha512-NNEz1sEZz1mBpgf6Tz3aJ9b8KjqpTiMYhHfCYA9h9Ipe4D8gUmOsvPHPK2M755OX7p7PmUmzp1XCUHYrZMVHRw=="],
+ "@angular/core": ["@angular/core@20.0.0", "", { "dependencies": { "tslib": "^2.3.0" }, "peerDependencies": { "@angular/compiler": "20.0.0", "rxjs": "^6.5.3 || ^7.4.0", "zone.js": "~0.15.0" }, "optionalPeers": ["@angular/compiler", "zone.js"] }, "sha512-2UjKbTtYSY8omY+LE4G6hQ1/R4PkE6NY7/2u99TxLH/oOnc9broCH1g9ITU+n0eJURcOFeK0/w6RdSrK+di3pg=="],
- "@angular/forms": ["@angular/forms@19.2.5", "", { "dependencies": { "tslib": "^2.3.0" }, "peerDependencies": { "@angular/common": "19.2.5", "@angular/core": "19.2.5", "@angular/platform-browser": "19.2.5", "rxjs": "^6.5.3 || ^7.4.0" } }, "sha512-2Zvy3qK1kOxiAX9fdSaeG48q7oyO/4RlMYlg1w+ra9qX1SrgwF3OQ2P2Vs+ojg1AxN3z9xFp4aYaaID/G2LZAw=="],
+ "@angular/forms": ["@angular/forms@20.0.0", "", { "dependencies": { "tslib": "^2.3.0" }, "peerDependencies": { "@angular/common": "20.0.0", "@angular/core": "20.0.0", "@angular/platform-browser": "20.0.0", "rxjs": "^6.5.3 || ^7.4.0" } }, "sha512-6yeb99IrNyeyj7o0bbd+n3JTZrXX2dJfdYLJH3tlXVlO9wg63bq+YR1AeM+RDCYMs+YDJis0lQpF6s+OICJv4g=="],
- "@angular/platform-browser": ["@angular/platform-browser@19.2.5", "", { "dependencies": { "tslib": "^2.3.0" }, "peerDependencies": { "@angular/animations": "19.2.5", "@angular/common": "19.2.5", "@angular/core": "19.2.5" }, "optionalPeers": ["@angular/animations"] }, "sha512-Lshy++X16cvl6OPvfzMySpsqEaCPKEJmDjz7q7oSt96oxlh6LvOeOUVLjsNyrNaIt9NadpWoqjlu/I9RTPJkpw=="],
+ "@angular/platform-browser": ["@angular/platform-browser@20.0.0", "", { "dependencies": { "tslib": "^2.3.0" }, "peerDependencies": { "@angular/animations": "20.0.0", "@angular/common": "20.0.0", "@angular/core": "20.0.0" }, "optionalPeers": ["@angular/animations"] }, "sha512-FP9YjT2beF0tov0wub6+eUQqJd2MwyYqEQQ6+Qx67ukd04plIryhrcImORehrsN24DbnHkyTqhCvUyNAZs2uwA=="],
- "@angular/platform-browser-dynamic": ["@angular/platform-browser-dynamic@19.2.5", "", { "dependencies": { "tslib": "^2.3.0" }, "peerDependencies": { "@angular/common": "19.2.5", "@angular/compiler": "19.2.5", "@angular/core": "19.2.5", "@angular/platform-browser": "19.2.5" } }, "sha512-15in8u4552EcdWNTXY2h0MKuJbk3AuXwWr0zVTum4CfB/Ss2tNTrDEdWhgAbhnUI0e9jZQee/fhBbA1rleMYrA=="],
+ "@angular/platform-browser-dynamic": ["@angular/platform-browser-dynamic@20.0.0", "", { "dependencies": { "tslib": "^2.3.0" }, "peerDependencies": { "@angular/common": "20.0.0", "@angular/compiler": "20.0.0", "@angular/core": "20.0.0", "@angular/platform-browser": "20.0.0" } }, "sha512-AACq3Ijuq59SdLDmfxWU8hYlo8O4Br9OHWNAga2W0X6p/7HlpeZZVdTlb/KGVYRKJvGpgSB10QYlRPfm215q9Q=="],
- "@angular/router": ["@angular/router@19.2.5", "", { "dependencies": { "tslib": "^2.3.0" }, "peerDependencies": { "@angular/common": "19.2.5", "@angular/core": "19.2.5", "@angular/platform-browser": "19.2.5", "rxjs": "^6.5.3 || ^7.4.0" } }, "sha512-9pSfmdNXLjaOKj0kd4UxBC7sFdCFOnRGbftp397G3KWqsLsGSKmNFzqhXNeA5QHkaVxnpmpm8HzXU+zYV5JwSg=="],
+ "@angular/router": ["@angular/router@20.0.0", "", { "dependencies": { "tslib": "^2.3.0" }, "peerDependencies": { "@angular/common": "20.0.0", "@angular/core": "20.0.0", "@angular/platform-browser": "20.0.0", "rxjs": "^6.5.3 || ^7.4.0" } }, "sha512-RQ7rU4NaZDSvvOfMZQmB50q7de+jrHYb+f0ExLKBvr80B1MK3oc9VvI2BzBkGfM4aGx71MMa0UizjOiT/31kqw=="],
"@babel/code-frame": ["@babel/code-frame@7.26.2", "", { "dependencies": { "@babel/helper-validator-identifier": "^7.25.9", "js-tokens": "^4.0.0", "picocolors": "^1.0.0" } }, "sha512-RJlIHRueQgwWitWgF8OdFYGZX328Ax5BCemNGlqHfplnRT9ESi8JkFlvaVYbS+UubVY6dpv87Fs2u5M29iNFVQ=="],
@@ -352,7 +352,7 @@
"@eslint/plugin-kit": ["@eslint/plugin-kit@0.2.8", "", { "dependencies": { "@eslint/core": "^0.13.0", "levn": "^0.4.1" } }, "sha512-ZAoA40rNMPwSm+AeHpCq8STiNAwzWLJuP8Xv4CHIc9wv/PSuExjMrmjfYNj682vW0OOiZ1HKxzvjQr9XZIisQA=="],
- "@fortawesome/angular-fontawesome": ["@fortawesome/angular-fontawesome@1.0.0", "", { "dependencies": { "@fortawesome/fontawesome-svg-core": "^6.7.1", "tslib": "^2.8.1" }, "peerDependencies": { "@angular/core": "^19.0.0" } }, "sha512-EC2fYuXIuw2ld1kzJi+zysWus6OeGGfLQtbh0hW9zyyq5aBo8ZJkcJKBsVQ8E6Mg7nHyTWaXn+sdcXTPDWz+UQ=="],
+ "@fortawesome/angular-fontawesome": ["@fortawesome/angular-fontawesome@2.0.0", "", { "dependencies": { "@fortawesome/fontawesome-svg-core": "^6.7.2", "tslib": "^2.8.1" }, "peerDependencies": { "@angular/core": "^20.0.0" } }, "sha512-xsPfjcs7elZYWo1Q19IrqMd5GdUAyPGnwElq7x/x9PKIwYXsnCaEtJkMUkZUj3Yesdkmqs0X0IMOwZvz7BNAtw=="],
"@fortawesome/fontawesome-common-types": ["@fortawesome/fontawesome-common-types@6.7.2", "", {}, "sha512-Zs+YeHUC5fkt7Mg1l6XTniei3k4bwG/yo3iFUtZWd/pMx9g3fdvkSK9E0FOC+++phXOka78uJcYb8JaFkW52Xg=="],
@@ -1868,8 +1868,6 @@
"yoctocolors-cjs": ["yoctocolors-cjs@2.1.2", "", {}, "sha512-cYVsTjKl8b+FrnidjibDWskAv7UKOfcwaVZdp/it9n1s9fU3IkgDbhdIRKCW4JDsAlECJY0ytoVPT3sK6kideA=="],
- "zone.js": ["zone.js@0.15.0", "", {}, "sha512-9oxn0IIjbCZkJ67L+LkhYWRyAy7axphb3VgE2MBDlOqnmHMPWGYMxJxBYFueFq/JGY2GMwS0rU+UCLunEmy5UA=="],
-
"@angular-devkit/architect/rxjs": ["rxjs@7.8.1", "", { "dependencies": { "tslib": "^2.1.0" } }, "sha512-AA3TVj+0A2iuIoQkWEK/tqFjBq2j+6PO6Y0zJcvzLAFhEFIO3HL0vls9hWLncZbAAbK0mar7oZ4V079I/qPMxg=="],
"@angular-devkit/build-angular/postcss": ["postcss@8.5.2", "", { "dependencies": { "nanoid": "^3.3.8", "picocolors": "^1.1.1", "source-map-js": "^1.2.1" } }, "sha512-MjOadfU3Ys9KYoX0AdkBlFEF1Vx37uCCeN4ZHnmwm9FfpbsGWMZeBLMmmpY+6Ocqod7mkdZ0DT31OlbsFrLlkA=="],
diff --git a/frontend/package.json b/frontend/package.json
index d5460c5..5de77bb 100644
--- a/frontend/package.json
+++ b/frontend/package.json
@@ -14,16 +14,16 @@
},
"private": true,
"dependencies": {
- "@angular/animations": "^19.0.0",
- "@angular/cdk": "~19.2.0",
- "@angular/common": "^19.0.0",
- "@angular/compiler": "^19.2.4",
- "@angular/core": "^19.0.0",
- "@angular/forms": "^19.0.0",
- "@angular/platform-browser": "^19.0.0",
- "@angular/platform-browser-dynamic": "^19.0.0",
- "@angular/router": "^19.0.0",
- "@fortawesome/angular-fontawesome": "^1.0.0",
+ "@angular/animations": "^20.0.0",
+ "@angular/cdk": "~20.0.0",
+ "@angular/common": "^20.0.0",
+ "@angular/compiler": "^20.0.0",
+ "@angular/core": "^20.0.0",
+ "@angular/forms": "^20.0.0",
+ "@angular/platform-browser": "^20.0.0",
+ "@angular/platform-browser-dynamic": "^20.0.0",
+ "@angular/router": "^20.0.0",
+ "@fortawesome/angular-fontawesome": "^2.0.0",
"@fortawesome/fontawesome-svg-core": "^6.7.2",
"@fortawesome/free-brands-svg-icons": "^6.7.2",
"@fortawesome/free-solid-svg-icons": "^6.7.2",