using GerbilManagerWebAPI.Dtos; using GerbilManagerWebAPI.Models; using Microsoft.AspNetCore.Http.HttpResults; using Microsoft.EntityFrameworkCore; namespace GerbilManagerWebAPI.Endpoints { /// /// FEEDBACK: the "Fehler melden" report sink. /// POST /feedback -> persist a user bug report (with captured debug context), returns 201. /// GET /feedback -> list reports, newest first (for later review). /// Feedback is decoupled from gerbils/litters (loose nullable Guid columns, no FK), so /// rows survive the import re-ingest wipe. /// public static class FeedbackEndpoints { public static IEndpointRouteBuilder MapFeedbackEndpoints(this IEndpointRouteBuilder app) { var group = app.MapGroup("/feedback").WithTags("Feedback"); group.MapPost("/", async Task, BadRequest>> ( FeedbackInput input, ApplicationContext db, HttpContext http) => { if (string.IsNullOrWhiteSpace(input.Message)) return TypedResults.BadRequest("Message darf nicht leer sein."); var entity = new Feedback { Id = Guid.NewGuid(), Message = input.Message.Trim(), Context = string.IsNullOrWhiteSpace(input.Context) ? "unknown" : input.Context.Trim(), GerbilId = input.GerbilId, LitterId = input.LitterId, EntityName = input.EntityName, Url = input.Url, ClientTimestamp = input.ClientTimestamp, UserAgent = http.Request.Headers.UserAgent.ToString() is { Length: > 0 } ua ? ua : null, CreatedAt = DateTimeOffset.UtcNow, }; db.Feedback.Add(entity); await db.SaveChangesAsync(); return TypedResults.Created($"/feedback/{entity.Id}", ToDto(entity)); }); group.MapGet("/", async (ApplicationContext db) => { // Order in memory: SQLite (test host) cannot ORDER BY a DateTimeOffset column. var rows = await db.Feedback.AsNoTracking().ToListAsync(); return TypedResults.Ok(rows .OrderByDescending(f => f.CreatedAt) .Select(ToDto) .ToList()); }); return app; } private static FeedbackDto ToDto(Feedback f) => new(f.Id, f.Message, f.Context, f.GerbilId, f.LitterId, f.EntityName, f.Url, f.ClientTimestamp, f.UserAgent, f.CreatedAt); } }