diff --git a/Koogle.Application/Class1.cs b/Koogle.Application/Class1.cs
deleted file mode 100644
index ebefdfc..0000000
--- a/Koogle.Application/Class1.cs
+++ /dev/null
@@ -1,7 +0,0 @@
-namespace Koogle.Application
-{
- public class Class1
- {
-
- }
-}
diff --git a/Koogle.Application/DTOs/DayDto.cs b/Koogle.Application/DTOs/DayDto.cs
new file mode 100644
index 0000000..883fa30
--- /dev/null
+++ b/Koogle.Application/DTOs/DayDto.cs
@@ -0,0 +1,59 @@
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+using System.Threading.Tasks;
+using Koogle.Domain.Enums;
+
+namespace Koogle.Application.DTOs
+{
+ public record DayDto
+ {
+ public int Id { get; set; }
+
+ public DateTime PostDate { get; set; }
+
+ public DayStatus Status { get; set; }
+ }
+
+ public record DaySummaryDto
+ {
+ public int Id { get; set; }
+
+ public DateTime PostDate { get; set; }
+
+ public DayStatus Status { get; set; }
+ }
+
+ public record DayFilterDto
+ {
+ public int Year { get; set; }
+
+ /////
+ ///// Sortierfeld.
+ /////
+ //public GoalSortField SortBy { get; init; } = GoalSortField.Name;
+
+ /////
+ ///// Sortierrichtung.
+ /////
+ //public SortDirection SortDirection { get; init; } = SortDirection.Ascending;
+
+ // Paginierung
+
+ ///
+ /// Seitennummer (1-basiert).
+ ///
+ public int Page { get; init; } = 1;
+
+ ///
+ /// Anzahl der Einträge pro Seite.
+ ///
+ public int PageSize { get; init; } = 20;
+
+ ///
+ /// Berechnet den Skip-Wert für die Paginierung.
+ ///
+ public int Skip => (Page - 1) * PageSize;
+ }
+}
diff --git a/Koogle.Application/DTOs/PagedResultDto.cs b/Koogle.Application/DTOs/PagedResultDto.cs
new file mode 100644
index 0000000..5003d01
--- /dev/null
+++ b/Koogle.Application/DTOs/PagedResultDto.cs
@@ -0,0 +1,50 @@
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+using System.Threading.Tasks;
+
+namespace Koogle.Application.DTOs
+{
+ ///
+ /// Ergebnis einer paginierten Abfrage.
+ ///
+ /// Typ der Ergebnisse.
+ public record PagedResultDto
+ {
+ ///
+ /// Die Ergebnisse der aktuellen Seite.
+ ///
+ public IReadOnlyList Items { get; init; } = [];
+
+ ///
+ /// Gesamtanzahl der Ergebnisse (über alle Seiten).
+ ///
+ public int TotalCount { get; init; }
+
+ ///
+ /// Aktuelle Seitennummer.
+ ///
+ public int Page { get; init; }
+
+ ///
+ /// Anzahl der Einträge pro Seite.
+ ///
+ public int PageSize { get; init; }
+
+ ///
+ /// Gesamtanzahl der Seiten.
+ ///
+ public int TotalPages => PageSize > 0 ? (int)Math.Ceiling((double)TotalCount / PageSize) : 0;
+
+ ///
+ /// Gibt an, ob es eine vorherige Seite gibt.
+ ///
+ public bool HasPreviousPage => Page > 1;
+
+ ///
+ /// Gibt an, ob es eine nächste Seite gibt.
+ ///
+ public bool HasNextPage => Page < TotalPages;
+ }
+}
diff --git a/Koogle.Application/DependencyInjection.cs b/Koogle.Application/DependencyInjection.cs
new file mode 100644
index 0000000..93da18e
--- /dev/null
+++ b/Koogle.Application/DependencyInjection.cs
@@ -0,0 +1,36 @@
+using Koogle.Application.Interfaces;
+using Koogle.Application.Services;
+using Microsoft.AspNetCore.Authorization;
+using Microsoft.Extensions.DependencyInjection;
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Reflection;
+using System.Text;
+using System.Threading.Tasks;
+
+namespace Koogle.Application
+{
+ public static class DependencyInjection
+ {
+ public static IServiceCollection AddApplication(this IServiceCollection services)
+ {
+ // AutoMapper
+ services.AddAutoMapper(cfg => cfg.LicenseKey = "eyJhbGciOiJSUzI1NiIsImtpZCI6Ikx1Y2t5UGVubnlTb2Z0d2FyZUxpY2Vuc2VLZXkvYmJiMTNhY2I1OTkwNGQ4OWI0Y2IxYzg1ZjA4OGNjZjkiLCJ0eXAiOiJKV1QifQ.eyJpc3MiOiJodHRwczovL2x1Y2t5cGVubnlzb2Z0d2FyZS5jb20iLCJhdWQiOiJMdWNreVBlbm55U29mdHdhcmUiLCJleHAiOiIxNzk3MjkyODAwIiwiaWF0IjoiMTc2NTgyNzkzMyIsImFjY291bnRfaWQiOiIwMTliMjM4YjYxZGM3MWYwOTAyYmY5OGU0NzNmOTY5ZSIsImN1c3RvbWVyX2lkIjoiY3RtXzAxa2NocnF3dHNkczM4cjJnOTBncmZ5ajA1Iiwic3ViX2lkIjoiLSIsImVkaXRpb24iOiIwIiwidHlwZSI6IjIifQ.ehnUm61bFyXv2s0RScHd3vV2wIRivFN-phslB65UxztWBsk1EAtqTgPT55ONQ6-k7zi7G1vpLUUz9NL4EfpMRwgl1obeCTrs1pzvIRkednzrSdcPKAOmil-xiCS1TlrvaLzLnBWCr0JroGrAmtMYjsfUpYayRx9x8BzjUGBPUvb6eUR6wSbEtPzZbycgsp4Oj4Wwi23o56UGWHdNz7R8ofKy9EyzrgiG1uYfJZUDB_B5uWtdWi5M2bfoYHcLj-7VbSJlVlW2RoETEYuylBjG0Bwg_ZtSoVsCuqi_qV_GuyBOKbPpjFK4NFcInYFkAxAyzV_uTaFQpCFukPpCMZpFtA",
+ Assembly.GetExecutingAssembly());
+
+ // FluentValidation
+ //services.AddValidatorsFromAssembly(Assembly.GetExecutingAssembly());
+
+ // Application Services
+ services.AddScoped();
+ //services.AddScoped();
+ //services.AddScoped();
+ //services.AddScoped();
+ //services.AddScoped();
+ //services.AddScoped();
+
+ return services;
+ }
+ }
+}
diff --git a/Koogle.Application/Interfaces/IDayService.cs b/Koogle.Application/Interfaces/IDayService.cs
new file mode 100644
index 0000000..2300572
--- /dev/null
+++ b/Koogle.Application/Interfaces/IDayService.cs
@@ -0,0 +1,14 @@
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+using System.Threading.Tasks;
+using Koogle.Application.DTOs;
+
+namespace Koogle.Application.Interfaces
+{
+ public interface IDayService
+ {
+ Task> GetAllAsync(DayFilterDto filter, CancellationToken cancellationToken = default);
+ }
+}
diff --git a/Koogle.Application/Koogle.Application.csproj b/Koogle.Application/Koogle.Application.csproj
index 125f4c9..5a2d68f 100644
--- a/Koogle.Application/Koogle.Application.csproj
+++ b/Koogle.Application/Koogle.Application.csproj
@@ -6,4 +6,13 @@
enable
+
+
+
+
+
+
+
+
+
diff --git a/Koogle.Application/Services/DayService.cs b/Koogle.Application/Services/DayService.cs
new file mode 100644
index 0000000..a43a6f3
--- /dev/null
+++ b/Koogle.Application/Services/DayService.cs
@@ -0,0 +1,135 @@
+using Koogle.Application.DTOs;
+using Koogle.Application.Interfaces;
+using Koogle.Domain.Interfaces;
+using KoogleApp.Data;
+using Microsoft.AspNetCore.Authorization;
+using Microsoft.EntityFrameworkCore;
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+using System.Threading.Tasks;
+using AutoMapper;
+using KoogleApp.Entities;
+
+namespace Koogle.Application.Services
+{
+ public class DayService : IDayService
+ {
+ private readonly IDbContextFactory _contextFactory;
+ private readonly IDayRepository _goalRepository;
+ private readonly ICurrentUserService _currentUserService;
+ private readonly IAuthorizationService _authorizationService;
+ private readonly IMapper _mapper;
+
+ public DayService(IDbContextFactory contextFactory,
+ IDayRepository goalRepository,
+ IMapper mapper)
+ {
+
+ }
+ public async Task> GetAllAsync(DayFilterDto filter, CancellationToken cancellationToken = default)
+ {
+ await using var context = await _contextFactory.CreateDbContextAsync(cancellationToken);
+
+ var query = context.Days
+ .Include(g => g.PostDate)
+ .Include(g => g.Status)
+ //.Include(g => g.ActualValues.Where(av => !av.IsDeleted))
+ .AsQueryable();
+
+ // Filter anwenden
+ query = ApplyFilters(query, filter);
+
+ // Gesamtanzahl ermitteln
+ var totalCount = await query.CountAsync(cancellationToken);
+
+ // Sortierung anwenden
+ //query = ApplySorting(query, filter);
+
+ // Paginierung anwenden
+ var goals = await query
+ .Skip(filter.Skip)
+ .Take(filter.PageSize)
+ .ToListAsync(cancellationToken);
+
+ var items = _mapper.Map>(goals);
+
+ return new PagedResultDto
+ {
+ Items = items,
+ TotalCount = totalCount,
+ Page = filter.Page,
+ PageSize = filter.PageSize
+ };
+ }
+
+ ///
+ /// Wendet Filter auf die Abfrage an.
+ ///
+ private static IQueryable ApplyFilters(IQueryable query, DayFilterDto filter)
+ {
+ //if (!string.IsNullOrWhiteSpace(filter.SearchTerm))
+ //{
+ // var searchTerm = filter.SearchTerm.ToLower();
+ // query = query.Where(g =>
+ // g.Name.ToLower().Contains(searchTerm) ||
+ // (g.Description != null && g.Description.ToLower().Contains(searchTerm)));
+ //}
+
+ //if (filter.CompanyId.HasValue)
+ //{
+ // query = query.Where(g => g.CompanyId == filter.CompanyId.Value);
+ //}
+
+ //if (filter.CompanyIds?.Count > 0)
+ //{
+ // query = query.Where(g => filter.CompanyIds.Contains(g.CompanyId));
+ //}
+
+ //if (filter.YearId.HasValue)
+ //{
+ // query = query.Where(g => g.YearId == filter.YearId.Value);
+ //}
+
+ //if (filter.Year.HasValue)
+ //{
+ // query = query.Where(g => g.Year.YearNumber == filter.Year.Value);
+ //}
+
+ //if (filter.GoalType.HasValue)
+ //{
+ // query = query.Where(g => g.GoalType == filter.GoalType.Value);
+ //}
+
+ //if (filter.TagId.HasValue)
+ //{
+ // query = query.Where(g => g.Tags.Any(t => t.Id == filter.TagId.Value));
+ //}
+
+ //if (filter.TagIds?.Count > 0)
+ //{
+ // query = query.Where(g => g.Tags.Any(t => filter.TagIds.Contains(t.Id)));
+ //}
+
+ //if (filter.OwnerId.HasValue)
+ //{
+ // query = query.Where(g => g.GoalOwners.Any(go => go.UserId == filter.OwnerId.Value));
+ //}
+
+ //if (filter.IsInherited.HasValue)
+ //{
+ // query = filter.IsInherited.Value
+ // ? query.Where(g => g.ParentGoalId != null)
+ // : query.Where(g => g.ParentGoalId == null);
+ //}
+
+ //if (filter.IsRootGoal == true)
+ //{
+ // query = query.Where(g => g.ParentGoalId == null);
+ //}
+
+ return query;
+ }
+ }
+}
diff --git a/Koogle.Infrastrcuture/DependencyInjection.cs b/Koogle.Infrastrcuture/DependencyInjection.cs
index e6c91f1..0e9077f 100644
--- a/Koogle.Infrastrcuture/DependencyInjection.cs
+++ b/Koogle.Infrastrcuture/DependencyInjection.cs
@@ -1,12 +1,40 @@
-using System;
+using KoogleApp.Data;
+using Microsoft.AspNetCore.Authorization;
+using Microsoft.AspNetCore.Identity;
+using Microsoft.EntityFrameworkCore;
+using Microsoft.Extensions.Configuration;
+using Microsoft.Extensions.DependencyInjection;
+using System;
using System.Collections.Generic;
using System.Linq;
+using System.Reflection;
using System.Text;
using System.Threading.Tasks;
namespace Koogle.Infrastrcuture
{
- internal class DependencyInjection
+ public static class DependencyInjection
{
+ public static IServiceCollection AddInfrastructure(this IServiceCollection services,
+ IConfiguration configuration)
+ {
+ var connectionString = configuration.GetConnectionString("AppDb") ?? throw new InvalidOperationException("Connection string 'AppDb' not found.");
+
+ //services.AddDbContext(options =>
+ // options.UseSqlServer(connectionString));
+
+ services.AddDatabaseDeveloperPageExceptionFilter();
+
+ services.AddIdentityCore(options => options.SignIn.RequireConfirmedAccount = true)
+ .AddEntityFrameworkStores()
+ .AddSignInManager()
+ .AddDefaultTokenProviders();
+
+ services.AddDbContextFactory(options =>
+ options.UseSqlServer(connectionString)
+ );
+
+ return services;
+ }
}
}
diff --git a/Koogle.Tests/ReducerTests/ThrowPanelStateTests.cs b/Koogle.Tests/ReducerTests/ThrowPanelStateTests.cs
index b29293b..d499131 100644
--- a/Koogle.Tests/ReducerTests/ThrowPanelStateTests.cs
+++ b/Koogle.Tests/ReducerTests/ThrowPanelStateTests.cs
@@ -17,7 +17,7 @@ namespace Koogle.Tests.ReducerTests
{
// Arrange
var initialState = new ThrowPanelState();
- var action = new StartStopAction(initialState, new TrainingSetupState());
+ var action = new StartStopAction(new TrainingSetupState());
// Act
var newState = ThrowPanelStateReducer.OnStartStop(initialState, action);
@@ -31,7 +31,7 @@ namespace Koogle.Tests.ReducerTests
{
// Arrange
var initialState = new ThrowPanelState();
- var action = new StartStopAction(initialState, null);
+ var action = new StartStopAction(null);
// Act
var newState = ThrowPanelStateReducer.OnStartStop(initialState, action);
@@ -469,7 +469,7 @@ namespace Koogle.Tests.ReducerTests
// Arrange
var initialState = new ThrowPanelState();
var setupState = new TrainingSetupState() { ThrowMode = ThrowMode.Decrease };
- var action = new StartStopAction(initialState, setupState);
+ var action = new StartStopAction(setupState);
// Act
var newState = ThrowPanelStateReducer.OnStartStop(initialState, action);
@@ -484,7 +484,7 @@ namespace Koogle.Tests.ReducerTests
// Arrange
var initialState = new ThrowPanelState();
var setupState = new TrainingSetupState() { ThrowsPerRound = 5 };
- var action = new StartStopAction(initialState, setupState);
+ var action = new StartStopAction(setupState);
// Act
var newState = ThrowPanelStateReducer.OnStartStop(initialState, action);
@@ -499,7 +499,7 @@ namespace Koogle.Tests.ReducerTests
// Arrange
var initialState = new ThrowPanelState();
var setupState = new TrainingSetupState() { DayId = 123 };
- var action = new StartStopAction(initialState, setupState);
+ var action = new StartStopAction(setupState);
// Act
var newState = ThrowPanelStateReducer.OnStartStop(initialState, action);
@@ -514,7 +514,7 @@ namespace Koogle.Tests.ReducerTests
// Arrange
var initialState = new ThrowPanelState();
var setupState = new TrainingSetupState();
- var action = new StartStopAction(initialState, setupState);
+ var action = new StartStopAction(setupState);
// Act
var newState = ThrowPanelStateReducer.OnStartStop(initialState, action);
@@ -528,7 +528,7 @@ namespace Koogle.Tests.ReducerTests
{
// Arrange
var initialState = new ThrowPanelState() with { IsStated = true };
- var action = new StartStopAction(initialState, null);
+ var action = new StartStopAction(null);
// Act
var newState = ThrowPanelStateReducer.OnStartStop(initialState, action);
diff --git a/KoogleApp/Program.cs b/KoogleApp/Program.cs
index e6a93a3..cacf5f4 100644
--- a/KoogleApp/Program.cs
+++ b/KoogleApp/Program.cs
@@ -1,3 +1,5 @@
+using Koogle.Application;
+using Koogle.Infrastrcuture;
using KoogleApp.Components;
using KoogleApp.Components.Account;
using KoogleApp.Data;
@@ -39,8 +41,12 @@ builder.Services.AddAuthentication(options =>
})
.AddIdentityCookies();
+// Infrastructure Layer (DbContext, Repositories, CurrentUserService)
+builder.Services.AddInfrastructure(builder.Configuration);
+
+// Application Layer (Services, AutoMapper, Validators)
+builder.Services.AddApplication();
-builder.Services.AddDbServices(builder.Configuration);
builder.Services.AddSingleton, IdentityNoOpEmailSender>();
diff --git a/KoogleApp/Services/ServiceExtension.cs b/KoogleApp/Services/ServiceExtension.cs
index e57737f..4d8c500 100644
--- a/KoogleApp/Services/ServiceExtension.cs
+++ b/KoogleApp/Services/ServiceExtension.cs
@@ -14,28 +14,6 @@ namespace KoogleApp.Services
{
public static class ServiceExtension
{
- public static IServiceCollection AddDbServices(this IServiceCollection services, IConfiguration configuration)
- {
- var connectionString = configuration.GetConnectionString("AppDb") ?? throw new InvalidOperationException("Connection string 'AppDb' not found.");
-
- //services.AddDbContext(options =>
- // options.UseSqlServer(connectionString));
-
- services.AddDatabaseDeveloperPageExceptionFilter();
-
- services.AddIdentityCore(options => options.SignIn.RequireConfirmedAccount = true)
- .AddEntityFrameworkStores()
- .AddSignInManager()
- .AddDefaultTokenProviders();
-
- services.AddDbContextFactory(options =>
- options.UseSqlServer(connectionString)
- );
-
-
- return services;
- }
-
public static IServiceCollection AddAppServices(this IServiceCollection services)
{
services.AddFluxor(x =>