@if (authService.userInfo(); as userInfo) {
@if (navService.isAccountDropdownOpen()) {
diff --git a/UI/Web/src/app/nav-bar/nav-bar.component.ts b/UI/Web/src/app/nav-bar/nav-bar.component.ts
index d6933c4..6adb9bc 100644
--- a/UI/Web/src/app/nav-bar/nav-bar.component.ts
+++ b/UI/Web/src/app/nav-bar/nav-bar.component.ts
@@ -1,13 +1,4 @@
-import {
- ChangeDetectionStrategy,
- Component,
- computed,
- HostListener,
- inject,
- OnInit,
- Signal,
- signal
-} from '@angular/core';
+import {ChangeDetectionStrategy, Component, computed, HostListener, inject, OnInit, Signal} from '@angular/core';
import {NgTemplateOutlet, TitleCasePipe} from "@angular/common";
import {animate, style, transition, trigger} from "@angular/animations";
import {RouterLink} from '@angular/router';
diff --git a/UI/Web/src/app/oidc/oidc-callback/oidc-callback.component.html b/UI/Web/src/app/oidc/oidc-callback/oidc-callback.component.html
deleted file mode 100644
index 73aa564..0000000
--- a/UI/Web/src/app/oidc/oidc-callback/oidc-callback.component.html
+++ /dev/null
@@ -1,32 +0,0 @@
-
-
-
-
-
{{t('title')}}
-
-
-
-
-
-
-
- {{t('info')}}
-
-
- @if (false) {
-
- }
-
-
diff --git a/UI/Web/src/app/oidc/oidc-callback/oidc-callback.component.scss b/UI/Web/src/app/oidc/oidc-callback/oidc-callback.component.scss
deleted file mode 100644
index 56d6f4f..0000000
--- a/UI/Web/src/app/oidc/oidc-callback/oidc-callback.component.scss
+++ /dev/null
@@ -1,149 +0,0 @@
-.callback-container {
- min-height: 100vh;
- display: flex;
- align-items: center;
- justify-content: center;
- padding: 1rem;
-
- background-image:
- radial-gradient(circle at 20% 20%, var(--champagne) 0%, transparent 50%),
- radial-gradient(circle at 80% 80%, var(--surface-hover) 0%, transparent 50%);
-}
-
-.callback-content {
- text-align: center;
- max-width: 400px;
- width: 100%;
- padding: 2rem;
- background: var(--surface-card);
- border-radius: 16px;
- box-shadow: 0 8px 32px var(--shadow-light);
- border: 1px solid var(--surface-border);
-
- @media (min-width: 768px) {
- padding: 3rem;
- }
-}
-
-.callback-logo {
- margin-bottom: 2rem;
-
- .logo-circle {
- width: 80px;
- height: 80px;
- margin: 0 auto;
- border-radius: 50%;
- background: linear-gradient(135deg, var(--primary-color), var(--primary-color-light));
- display: flex;
- align-items: center;
- justify-content: center;
- box-shadow: 0 4px 16px var(--shadow-medium);
- animation: pulse 2s ease-in-out infinite;
-
- @media (min-width: 768px) {
- width: 96px;
- height: 96px;
- }
-
- i {
- font-size: 2rem;
- color: var(--text-light);
-
- @media (min-width: 768px) {
- font-size: 2.5rem;
- }
- }
- }
-}
-
-.callback-title {
- font-family: 'Spectral', serif;
- font-size: 1.75rem;
- font-weight: 600;
- color: var(--text-primary);
- margin-bottom: 1rem;
-
- @media (min-width: 768px) {
- font-size: 2rem;
- }
-}
-
-.callback-message {
- margin-bottom: 2rem;
-
- p {
- font-size: 1rem;
- color: var(--text-secondary);
- line-height: 1.5;
- margin: 0;
-
- @media (min-width: 768px) {
- font-size: 1.1rem;
- }
- }
-}
-
-.callback-info {
- margin-top: 2rem;
-
- small {
- font-size: 0.875rem;
- color: var(--text-secondary);
- opacity: 0.8;
- }
-}
-
-.error-message {
- margin-top: 2rem;
- padding: 1rem;
- background: linear-gradient(135deg, #fee2e2, #fecaca);
- border: 1px solid #fca5a5;
- border-radius: 8px;
- color: #dc2626;
- display: flex;
- align-items: center;
- gap: 0.5rem;
-
- i {
- font-size: 1.2rem;
- flex-shrink: 0;
- }
-
- p {
- margin: 0;
- font-size: 0.9rem;
- font-weight: 500;
- }
-}
-
-@keyframes pulse {
- 0%, 100% { transform: scale(1); }
- 50% { transform: scale(1.05); }
-}
-
-@media (prefers-reduced-motion: reduce) {
- .logo-circle {
- animation: none;
- }
-}
-
-@media (prefers-color-scheme: dark) {
- .callback-container {
- background: linear-gradient(135deg, var(--primary-color) 0%, var(--primary-color-light) 100%);
- }
-
- .callback-content {
- background: var(--primary-color-light);
- border-color: var(--primary-color-lighter);
- }
-
- .callback-title {
- color: var(--text-light);
- }
-
- .callback-message p,
- .callback-info small {
- color: var(--text-light);
- opacity: 0.9;
- }
-}
diff --git a/UI/Web/src/app/oidc/oidc-callback/oidc-callback.component.ts b/UI/Web/src/app/oidc/oidc-callback/oidc-callback.component.ts
deleted file mode 100644
index e5f08c9..0000000
--- a/UI/Web/src/app/oidc/oidc-callback/oidc-callback.component.ts
+++ /dev/null
@@ -1,56 +0,0 @@
-import {ChangeDetectionStrategy, Component, effect, inject, OnInit, signal} from '@angular/core';
-import {LoadingSpinnerComponent} from '../../shared/components/loading-spinner/loading-spinner.component';
-import {TranslocoDirective} from '@jsverse/transloco';
-import {AuthService} from '../../_services/auth.service';
-import {Router} from '@angular/router';
-import {AuthGuard} from '../../_guards/auth-guard';
-import {NavigationService} from '../../_services/navigation.service';
-
-@Component({
- selector: 'app-oidc-callback',
- imports: [
- LoadingSpinnerComponent,
- TranslocoDirective
- ],
- templateUrl: './oidc-callback.component.html',
- styleUrl: './oidc-callback.component.scss',
- changeDetection: ChangeDetectionStrategy.OnPush
-})
-export class OidcCallbackComponent implements OnInit {
-
- private readonly authService = inject(AuthService);
- private readonly navService = inject(NavigationService);
- private readonly router = inject(Router);
-
- showErrorMessage = signal(false);
-
- constructor() {
- effect(() => {
- if (this.authService.isAuthenticated()) {
- this.redirect();
- }
- });
- }
-
- ngOnInit(): void {
- this.navService.showNavBar.set(false);
-
- setTimeout(() => {
- if (!this.authService.isAuthenticated()) {
- this.showErrorMessage.set(true);
- setTimeout(() => this.authService.login(), 1000);
- }
- }, 1000);
- }
-
-
- private redirect() {
- const path = localStorage.getItem(AuthGuard.urlKey);
- if (path && path !== '') {
- this.router.navigate([path]);
- } else {
- this.router.navigate(['/dashboard']);
- }
- }
-
-}
diff --git a/UI/Web/src/app/shared/components/confirm-modal/confirm-modal.component.ts b/UI/Web/src/app/shared/components/confirm-modal/confirm-modal.component.ts
index 0b4f2e9..874d698 100644
--- a/UI/Web/src/app/shared/components/confirm-modal/confirm-modal.component.ts
+++ b/UI/Web/src/app/shared/components/confirm-modal/confirm-modal.component.ts
@@ -1,4 +1,4 @@
-import {ChangeDetectionStrategy, Component, ContentChild, inject, model, TemplateRef} from '@angular/core';
+import {ChangeDetectionStrategy, Component, inject, model, TemplateRef} from '@angular/core';
import {translate, TranslocoDirective} from '@jsverse/transloco';
import {Subject} from 'rxjs';
import {NgbActiveModal} from '@ng-bootstrap/ng-bootstrap';
diff --git a/UI/Web/src/app/shared/components/loading-spinner/loading-spinner.component.ts b/UI/Web/src/app/shared/components/loading-spinner/loading-spinner.component.ts
index 6f0d403..615fe05 100644
--- a/UI/Web/src/app/shared/components/loading-spinner/loading-spinner.component.ts
+++ b/UI/Web/src/app/shared/components/loading-spinner/loading-spinner.component.ts
@@ -1,5 +1,5 @@
import {Component, input} from '@angular/core';
-import { CommonModule } from '@angular/common';
+import {CommonModule} from '@angular/common';
type SpinnerSize = 'small' | 'medium' | 'large';
type SpinnerColour = 'primary' | 'secondary' | 'white';
diff --git a/UI/Web/src/app/shared/components/settings-item/settings-item.component.ts b/UI/Web/src/app/shared/components/settings-item/settings-item.component.ts
index c4ee07f..c40766f 100644
--- a/UI/Web/src/app/shared/components/settings-item/settings-item.component.ts
+++ b/UI/Web/src/app/shared/components/settings-item/settings-item.component.ts
@@ -4,10 +4,16 @@
import {
ChangeDetectionStrategy,
ChangeDetectorRef,
- Component, ContentChild,
- ElementRef, HostListener,
+ Component,
+ ContentChild,
+ ElementRef,
+ HostListener,
inject,
- input, model, OnChanges, SimpleChange, SimpleChanges,
+ input,
+ model,
+ OnChanges,
+ SimpleChange,
+ SimpleChanges,
TemplateRef
} from '@angular/core';
import {AbstractControl} from '@angular/forms';
diff --git a/UI/Web/src/app/shared/components/table/table.component.ts b/UI/Web/src/app/shared/components/table/table.component.ts
index 7e02f0b..25937b1 100644
--- a/UI/Web/src/app/shared/components/table/table.component.ts
+++ b/UI/Web/src/app/shared/components/table/table.component.ts
@@ -2,13 +2,15 @@ import {
ChangeDetectionStrategy,
Component,
computed,
- ContentChild, effect, EventEmitter,
+ ContentChild,
+ EventEmitter,
input,
- OnInit, Output,
+ OnInit,
+ Output,
signal,
TemplateRef
} from '@angular/core';
-import {NgTemplateOutlet, TitleCasePipe} from '@angular/common';
+import {NgTemplateOutlet} from '@angular/common';
import {CdkDrag, CdkDragDrop, CdkDropList} from '@angular/cdk/drag-drop';
diff --git a/UI/Web/src/app/type-ahead/typeahead.component.ts b/UI/Web/src/app/type-ahead/typeahead.component.ts
index 3b712ce..9f43450 100644
--- a/UI/Web/src/app/type-ahead/typeahead.component.ts
+++ b/UI/Web/src/app/type-ahead/typeahead.component.ts
@@ -1,24 +1,32 @@
import {
- ChangeDetectionStrategy, ChangeDetectorRef,
- Component, computed,
- ContentChild, DestroyRef, effect, ElementRef,
+ ChangeDetectionStrategy,
+ Component,
+ computed,
+ ContentChild,
+ DestroyRef,
+ effect,
+ ElementRef,
EventEmitter,
inject,
input,
OnInit,
- Output, signal, TemplateRef,
+ Output,
+ signal,
+ TemplateRef,
ViewChild
} from '@angular/core';
import {
catchError,
debounceTime,
- distinctUntilChanged, filter,
+ distinctUntilChanged,
+ filter,
Observable,
of,
startWith,
Subject,
switchMap,
- take, tap
+ take,
+ tap
} from 'rxjs';
import {FormControl, ReactiveFormsModule} from '@angular/forms';
import {takeUntilDestroyed} from '@angular/core/rxjs-interop';
diff --git a/UI/Web/src/main.ts b/UI/Web/src/main.ts
index 6bb4616..8b0556a 100644
--- a/UI/Web/src/main.ts
+++ b/UI/Web/src/main.ts
@@ -1,8 +1,8 @@
///
-import { bootstrapApplication } from '@angular/platform-browser';
-import { appConfig } from './app/app.config';
-import { App } from './app/app';
+import {bootstrapApplication} from '@angular/platform-browser';
+import {appConfig} from './app/app.config';
+import {App} from './app/app';
bootstrapApplication(App, appConfig)
.catch((err) => console.error(err));
From 0e96db23a900f739f5ea0a8819108e9e95c2eca1 Mon Sep 17 00:00:00 2001
From: Amelia <77553571+Fesaa@users.noreply.github.com>
Date: Wed, 27 Aug 2025 23:16:04 +0200
Subject: [PATCH 02/15] I'm sorry for my sins, load base64 favicon from config
---
API/Startup.cs | 27 ++++++++++++++++++++++++++-
1 file changed, 26 insertions(+), 1 deletion(-)
diff --git a/API/Startup.cs b/API/Startup.cs
index b95820c..ae906d7 100644
--- a/API/Startup.cs
+++ b/API/Startup.cs
@@ -1,4 +1,5 @@
-using System.IO.Compression;
+using System.Buffers.Text;
+using System.IO.Compression;
using System.Reflection;
using API.Data;
using API.Exceptions;
@@ -6,6 +7,7 @@
using API.Helpers;
using API.ManualMigrations;
using API.Middleware;
+using Flurl.Http;
using Microsoft.AspNetCore.Http.Features;
using Microsoft.AspNetCore.ResponseCompression;
using Microsoft.Extensions.Primitives;
@@ -205,6 +207,7 @@ public void Configure(IApplicationBuilder app, IServiceProvider serviceProvider,
{
try
{
+ OverrideFaviconIfSet(logger);
logger.LogInformation("{Name} - v{Version}", BuildInfo.AppName, BuildInfo.Version);
}
catch (Exception)
@@ -214,4 +217,26 @@ public void Configure(IApplicationBuilder app, IServiceProvider serviceProvider,
}
});
}
+
+ private void OverrideFaviconIfSet(ILogger
logger)
+ {
+ try
+ {
+ var favicon = cfg.GetValue("Favicon");
+ if (string.IsNullOrWhiteSpace(favicon)) return;
+
+ if (!Directory.Exists("wwwroot")) return;
+
+ var filePath = Path.Join("wwwroot", "favicon.ico");
+
+ var data = Convert.FromBase64String(favicon);;
+
+ File.WriteAllBytes(filePath, data);
+ logger.LogDebug("Overwriting favicon from configuration: {Data}", favicon);
+ }
+ catch (Exception ex)
+ {
+ logger.LogError(ex, "An error occurred during overwrite of favicon");
+ }
+ }
}
\ No newline at end of file
From e95d34153c2d4951da306bef91569dc6fea8cb5f Mon Sep 17 00:00:00 2001
From: Amelia <77553571+Fesaa@users.noreply.github.com>
Date: Wed, 27 Aug 2025 23:36:36 +0200
Subject: [PATCH 03/15] BVDK-44 Loading spinner stock & OneTime checkbox
---
.../browse-stock/browse-stock.component.html | 75 ++++++++++---------
.../browse-stock/browse-stock.component.ts | 4 +
.../manage-delivery.component.html | 55 ++++++++------
.../manage-delivery.component.ts | 9 +++
4 files changed, 84 insertions(+), 59 deletions(-)
diff --git a/UI/Web/src/app/browse-stock/browse-stock.component.html b/UI/Web/src/app/browse-stock/browse-stock.component.html
index ec8b671..8a65f3d 100644
--- a/UI/Web/src/app/browse-stock/browse-stock.component.html
+++ b/UI/Web/src/app/browse-stock/browse-stock.component.html
@@ -1,44 +1,47 @@
-
-
-
-
-
-
-
-
-
-
-
-
- {{ stock.name }} |
- {{ categoryName(stock.product.categoryId) }} |
- {{ stock.description }} |
-
- {{stock.quantity}}
- |
-
-
-
-
-
- @if (authService.roles().includes(Role.ManageStock)) {
- |
+
+
+ }
diff --git a/UI/Web/src/app/browse-stock/browse-stock.component.ts b/UI/Web/src/app/browse-stock/browse-stock.component.ts
index 686707e..61371e6 100644
--- a/UI/Web/src/app/browse-stock/browse-stock.component.ts
+++ b/UI/Web/src/app/browse-stock/browse-stock.component.ts
@@ -11,6 +11,7 @@ import {UpdateStockModalComponent} from './_components/update-stock-modal/update
import {AuthService, Role} from '../_services/auth.service';
import {ProductCategory} from '../_models/product';
import {ProductService} from '../_services/product.service';
+import {LoadingSpinnerComponent} from '../shared/components/loading-spinner/loading-spinner.component';
@Component({
selector: 'app-browse-stock',
@@ -18,6 +19,7 @@ import {ProductService} from '../_services/product.service';
BadgeComponent,
TableComponent,
TranslocoDirective,
+ LoadingSpinnerComponent,
],
templateUrl: './browse-stock.component.html',
styleUrl: './browse-stock.component.scss',
@@ -30,6 +32,7 @@ export class BrowseStockComponent implements OnInit {
private readonly stockService = inject(StockService);
private readonly modalService = inject(ModalService);
+ loading = signal(true);
stock = signal([]);
categories = signal([])
@@ -57,6 +60,7 @@ export class BrowseStockComponent implements OnInit {
private load() {
this.stockService.getAll().subscribe(stocks => {
this.stock.set(stocks);
+ this.loading.set(false);
});
}
diff --git a/UI/Web/src/app/manage-delivery/manage-delivery.component.html b/UI/Web/src/app/manage-delivery/manage-delivery.component.html
index 54e96f3..c5cc1fe 100644
--- a/UI/Web/src/app/manage-delivery/manage-delivery.component.html
+++ b/UI/Web/src/app/manage-delivery/manage-delivery.component.html
@@ -126,7 +126,7 @@
- @if (product.type === 0) {
+ @if (product.type === ProductType.Consumable) {
{{ t('consumable') }}
} @else {
{{ t('one-time') }}
@@ -135,31 +135,40 @@
-