Files
DailyMeals/MealPlan.Api/Services/IngredientCatalogService.cs
Piotr Kus f15bb7a916 * Extended functionalities
* Added different UIs
2026-06-24 21:43:40 +02:00

154 lines
5.4 KiB
C#

using MealPlan.Api.Data;
using MealPlan.Api.DTOs.IngredientCatalog;
using MealPlan.Api.Models;
using Microsoft.EntityFrameworkCore;
namespace MealPlan.Api.Services;
public class IngredientCatalogService : IIngredientCatalogService
{
private readonly AppDbContext _db;
public IngredientCatalogService(AppDbContext db) => _db = db;
public async Task<IReadOnlyList<IngredientCategoryDto>> GetCategoriesAsync(CancellationToken ct = default)
{
return await _db.IngredientCategories.AsNoTracking()
.Where(c => c.IsActive)
.OrderBy(c => c.SortOrder)
.ThenBy(c => c.Name)
.Select(c => new IngredientCategoryDto
{
Id = c.Id,
Code = c.Code,
Name = c.Name,
ItemCount = c.Items.Count(i => i.IsActive),
})
.ToListAsync(ct);
}
public async Task<IReadOnlyList<IngredientNutritionItemDto>> GetItemsAsync(
string? categoryCode,
string? search,
CancellationToken ct = default)
{
var query = _db.IngredientNutritionCatalog.AsNoTracking()
.Include(i => i.Category)
.Where(i => i.IsActive && i.Category!.IsActive);
if (!string.IsNullOrWhiteSpace(categoryCode))
{
var code = categoryCode.Trim().ToLowerInvariant();
query = query.Where(i => i.Category!.Code == code);
}
if (!string.IsNullOrWhiteSpace(search))
{
var term = search.Trim().ToLowerInvariant();
query = query.Where(i =>
i.Name.ToLower().Contains(term) ||
(i.NamePl != null && i.NamePl.ToLower().Contains(term)));
}
var rows = await query
.OrderBy(i => i.Category!.SortOrder)
.ThenBy(i => i.SortOrder)
.ThenBy(i => i.Name)
.ToListAsync(ct);
return rows.Select(MapItem).ToList();
}
public async Task<IngredientNutritionItemDto?> GetItemByIdAsync(int id, CancellationToken ct = default)
{
var row = await _db.IngredientNutritionCatalog.AsNoTracking()
.Include(i => i.Category)
.FirstOrDefaultAsync(i => i.Id == id && i.IsActive, ct);
return row is null ? null : MapItem(row);
}
public async Task<IngredientNutritionItemDto?> CreateItemAsync(
UpsertIngredientNutritionItemDto dto,
CancellationToken ct = default)
{
var category = await _db.IngredientCategories.AsNoTracking()
.FirstOrDefaultAsync(c => c.Id == dto.CategoryId && c.IsActive, ct);
if (category is null) return null;
var entity = new IngredientNutritionCatalogItem
{
CategoryId = dto.CategoryId,
Name = dto.Name.Trim(),
NamePl = string.IsNullOrWhiteSpace(dto.NamePl) ? null : dto.NamePl.Trim(),
CaloriesPer100G = dto.CaloriesPer100G,
ProteinGPer100G = dto.ProteinGPer100G,
FatGPer100G = dto.FatGPer100G,
CarbsGPer100G = dto.CarbsGPer100G,
FiberGPer100G = dto.FiberGPer100G,
SortOrder = dto.SortOrder,
IsActive = dto.IsActive,
};
_db.IngredientNutritionCatalog.Add(entity);
await _db.SaveChangesAsync(ct);
entity.Category = category;
return MapItem(entity);
}
public async Task<IngredientNutritionItemDto?> UpdateItemAsync(
int id,
UpsertIngredientNutritionItemDto dto,
CancellationToken ct = default)
{
var entity = await _db.IngredientNutritionCatalog
.Include(i => i.Category)
.FirstOrDefaultAsync(i => i.Id == id, ct);
if (entity is null) return null;
var category = await _db.IngredientCategories.AsNoTracking()
.FirstOrDefaultAsync(c => c.Id == dto.CategoryId && c.IsActive, ct);
if (category is null) return null;
entity.CategoryId = dto.CategoryId;
entity.Name = dto.Name.Trim();
entity.NamePl = string.IsNullOrWhiteSpace(dto.NamePl) ? null : dto.NamePl.Trim();
entity.CaloriesPer100G = dto.CaloriesPer100G;
entity.ProteinGPer100G = dto.ProteinGPer100G;
entity.FatGPer100G = dto.FatGPer100G;
entity.CarbsGPer100G = dto.CarbsGPer100G;
entity.FiberGPer100G = dto.FiberGPer100G;
entity.SortOrder = dto.SortOrder;
entity.IsActive = dto.IsActive;
await _db.SaveChangesAsync(ct);
entity.Category = category;
return MapItem(entity);
}
public async Task<bool> DeleteItemAsync(int id, CancellationToken ct = default)
{
var entity = await _db.IngredientNutritionCatalog.FirstOrDefaultAsync(i => i.Id == id, ct);
if (entity is null) return false;
entity.IsActive = false;
await _db.SaveChangesAsync(ct);
return true;
}
private static IngredientNutritionItemDto MapItem(IngredientNutritionCatalogItem item) => new()
{
Id = item.Id,
CategoryId = item.CategoryId,
CategoryCode = item.Category?.Code ?? string.Empty,
CategoryName = item.Category?.Name ?? string.Empty,
Name = item.Name,
NamePl = item.NamePl,
CaloriesPer100G = item.CaloriesPer100G,
ProteinGPer100G = item.ProteinGPer100G,
FatGPer100G = item.FatGPer100G,
CarbsGPer100G = item.CarbsGPer100G,
FiberGPer100G = item.FiberGPer100G,
};
}