154 lines
5.4 KiB
C#
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,
|
|
};
|
|
}
|