* Extended functionalities
* Added different UIs
This commit is contained in:
153
MealPlan.Api/Services/IngredientCatalogService.cs
Normal file
153
MealPlan.Api/Services/IngredientCatalogService.cs
Normal file
@@ -0,0 +1,153 @@
|
||||
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,
|
||||
};
|
||||
}
|
||||
Reference in New Issue
Block a user