* Extended functionalities

* Added different UIs
This commit is contained in:
2026-06-24 21:43:40 +02:00
parent 282f4864c4
commit f15bb7a916
348 changed files with 59057 additions and 498 deletions

View File

@@ -0,0 +1,41 @@
namespace MealPlan.Api.Models;
/// <summary>Meal slot when logging consumption. Extends recipe categories with snack.</summary>
public enum ConsumptionMealCategory
{
Breakfast = 0,
SecondBreakfast = 1,
Lunch = 2,
Dinner = 3,
Snack = 4,
}
public enum DietReminderType
{
Water = 0,
Meal = 1,
Snack = 2,
Custom = 3,
}
/// <summary>Bitmask for reminder days: Sun=1, Mon=2, Tue=4, Wed=8, Thu=16, Fri=32, Sat=64.</summary>
public static class ReminderDaysMask
{
public const int All = 127;
public static bool IncludesDay(int mask, DayOfWeek day)
{
var bit = day switch
{
DayOfWeek.Sunday => 1,
DayOfWeek.Monday => 2,
DayOfWeek.Tuesday => 4,
DayOfWeek.Wednesday => 8,
DayOfWeek.Thursday => 16,
DayOfWeek.Friday => 32,
DayOfWeek.Saturday => 64,
_ => 0,
};
return (mask & bit) != 0;
}
}

View File

@@ -0,0 +1,49 @@
using System.ComponentModel.DataAnnotations;
using System.ComponentModel.DataAnnotations.Schema;
namespace MealPlan.Api.Models;
[Table("DietGeneratorDraftMeals")]
public class DietGeneratorDraftMeal
{
[Column("Id")]
public int Id { get; set; }
[Column("SessionId")]
public int SessionId { get; set; }
[Column("PlanDate")]
public DateOnly PlanDate { get; set; }
[Column("MealCategory")]
public int MealCategory { get; set; }
[Column("RecipeId")]
public int RecipeId { get; set; }
[Column("Portions")]
public decimal Portions { get; set; } = 1;
[Column("Calories")]
public int? Calories { get; set; }
[Column("ProteinG")]
public decimal? ProteinG { get; set; }
[Column("FatG")]
public decimal? FatG { get; set; }
[Column("CarbsG")]
public decimal? CarbsG { get; set; }
[Column("IsLocked")]
public bool IsLocked { get; set; }
[Column("GenerationVersion")]
public int GenerationVersion { get; set; } = 1;
public DietGeneratorSession? Session { get; set; }
public Recipe? Recipe { get; set; }
public ICollection<DietGeneratorDraftMealIngredient> PlannedIngredients { get; set; } =
new List<DietGeneratorDraftMealIngredient>();
}

View File

@@ -0,0 +1,34 @@
using System.ComponentModel.DataAnnotations;
using System.ComponentModel.DataAnnotations.Schema;
namespace MealPlan.Api.Models;
[Table("DietGeneratorDraftMealIngredients")]
public class DietGeneratorDraftMealIngredient
{
[Column("Id")]
public int Id { get; set; }
[Column("DraftMealId")]
public int DraftMealId { get; set; }
[Column("SourceIngredientId")]
public int? SourceIngredientId { get; set; }
[Column("Name")]
[MaxLength(255)]
public string Name { get; set; } = string.Empty;
[Column("AmountGrams")]
public decimal? AmountGrams { get; set; }
[Column("Unit")]
[MaxLength(50)]
public string? Unit { get; set; }
[Column("SortOrder")]
public int SortOrder { get; set; }
public DietGeneratorDraftMeal? DraftMeal { get; set; }
public Ingredient? SourceIngredient { get; set; }
}

View File

@@ -0,0 +1,68 @@
using System.ComponentModel.DataAnnotations;
using System.ComponentModel.DataAnnotations.Schema;
namespace MealPlan.Api.Models;
[Table("DietGeneratorSessions")]
public class DietGeneratorSession
{
[Column("Id")]
public int Id { get; set; }
[Column("UserId")]
public Guid UserId { get; set; }
[Column("Status")]
[MaxLength(32)]
public string Status { get; set; } = DietGeneratorStatuses.MacrosPending;
[Column("ProposedCalories")]
public int? ProposedCalories { get; set; }
[Column("ProposedProteinG")]
public decimal? ProposedProteinG { get; set; }
[Column("ProposedFatG")]
public decimal? ProposedFatG { get; set; }
[Column("ProposedCarbsG")]
public decimal? ProposedCarbsG { get; set; }
[Column("MacroRationale")]
public string? MacroRationale { get; set; }
[Column("MacrosAcceptedAt")]
public DateTime? MacrosAcceptedAt { get; set; }
[Column("PlanStartDate")]
public DateOnly? PlanStartDate { get; set; }
[Column("PlanDayCount")]
public int PlanDayCount { get; set; } = 7;
[Column("CalorieToleranceKcal")]
public int CalorieToleranceKcal { get; set; } = 10;
[Column("PreferencesJson")]
public string? PreferencesJson { get; set; }
[Column("CommittedAt")]
public DateTime? CommittedAt { get; set; }
[Column("CreatedAt")]
public DateTime CreatedAt { get; set; }
[Column("UpdatedAt")]
public DateTime? UpdatedAt { get; set; }
public ICollection<DietGeneratorDraftMeal> DraftMeals { get; set; } = new List<DietGeneratorDraftMeal>();
}
public static class DietGeneratorStatuses
{
public const string MacrosPending = "macros_pending";
public const string MacrosAccepted = "macros_accepted";
public const string PlanDraft = "plan_draft";
public const string Committed = "committed";
public const string Abandoned = "abandoned";
}

View File

@@ -0,0 +1,45 @@
using System.ComponentModel.DataAnnotations;
using System.ComponentModel.DataAnnotations.Schema;
namespace MealPlan.Api.Models;
[Table("DietReminders")]
public class DietReminder
{
[Column("Id")]
public int Id { get; set; }
[Column("UserId")]
public Guid UserId { get; set; }
[Column("ReminderType")]
public int ReminderType { get; set; }
[Column("Title")]
[MaxLength(120)]
public string Title { get; set; } = string.Empty;
[Column("Message")]
[MaxLength(500)]
public string? Message { get; set; }
[Column("TimeOfDay")]
public TimeOnly TimeOfDay { get; set; }
[Column("DaysOfWeekMask")]
public int DaysOfWeekMask { get; set; } = ReminderDaysMask.All;
[Column("MealCategory")]
public int? MealCategory { get; set; }
[Column("IsEnabled")]
public bool IsEnabled { get; set; } = true;
[Column("CreatedAt")]
public DateTime CreatedAt { get; set; }
[Column("UpdatedAt")]
public DateTime? UpdatedAt { get; set; }
public ApplicationUser? User { get; set; }
}

View File

@@ -0,0 +1,49 @@
using System.ComponentModel.DataAnnotations;
using System.ComponentModel.DataAnnotations.Schema;
namespace MealPlan.Api.Models;
[Table("DietUserProfiles")]
public class DietUserProfile
{
[Column("UserId")]
public Guid UserId { get; set; }
[Column("HeightCm")]
public decimal HeightCm { get; set; }
[Column("WeightKg")]
public decimal WeightKg { get; set; }
[Column("Age")]
public int Age { get; set; }
[Column("Sex")]
[MaxLength(16)]
public string Sex { get; set; } = "male";
[Column("ActivityLevel")]
[MaxLength(32)]
public string ActivityLevel { get; set; } = "moderate";
[Column("Goal")]
[MaxLength(32)]
public string Goal { get; set; } = "maintain";
[Column("AllergiesNotes")]
public string? AllergiesNotes { get; set; }
[Column("DislikedFoods")]
public string? DislikedFoods { get; set; }
[Column("MealsPerDayMask")]
public int MealsPerDayMask { get; set; } = 15;
[Column("CreatedAt")]
public DateTime CreatedAt { get; set; }
[Column("UpdatedAt")]
public DateTime? UpdatedAt { get; set; }
public ApplicationUser? User { get; set; }
}

View File

@@ -0,0 +1,35 @@
using System.ComponentModel.DataAnnotations;
using System.ComponentModel.DataAnnotations.Schema;
namespace MealPlan.Api.Models;
[Table("DrinkCatalog")]
public class DrinkCatalogItem
{
[Column("Id")]
public int Id { get; set; }
[Column("Code")]
[MaxLength(32)]
public string Code { get; set; } = string.Empty;
[Column("Name")]
[MaxLength(100)]
public string Name { get; set; } = string.Empty;
[Column("DefaultVolumeMl")]
public int DefaultVolumeMl { get; set; }
[Column("CaloriesPer100Ml")]
public decimal? CaloriesPer100Ml { get; set; }
[Column("IconEmoji")]
[MaxLength(16)]
public string? IconEmoji { get; set; }
[Column("SortOrder")]
public int SortOrder { get; set; }
[Column("IsActive")]
public bool IsActive { get; set; }
}

View File

@@ -0,0 +1,39 @@
using System.ComponentModel.DataAnnotations;
using System.ComponentModel.DataAnnotations.Schema;
namespace MealPlan.Api.Models;
[Table("DrinkConsumptions")]
public class DrinkConsumption
{
[Column("Id")]
public int Id { get; set; }
[Column("UserId")]
public Guid UserId { get; set; }
[Column("DrinkCatalogId")]
public int? DrinkCatalogId { get; set; }
[Column("LogDate")]
public DateOnly LogDate { get; set; }
[Column("ConsumedAt")]
public DateTime ConsumedAt { get; set; }
[Column("Name")]
[MaxLength(255)]
public string Name { get; set; } = string.Empty;
[Column("VolumeMl")]
public int VolumeMl { get; set; }
[Column("Calories")]
public int? Calories { get; set; }
[Column("CreatedAt")]
public DateTime CreatedAt { get; set; }
public ApplicationUser? User { get; set; }
public DrinkCatalogItem? DrinkCatalog { get; set; }
}

View File

@@ -29,5 +29,9 @@ public class Ingredient
[Column("SortOrder")]
public int SortOrder { get; set; }
[Column("CatalogItemId")]
public int? CatalogItemId { get; set; }
public Recipe? Recipe { get; set; }
public IngredientNutritionCatalogItem? CatalogItem { get; set; }
}

View File

@@ -0,0 +1,27 @@
using System.ComponentModel.DataAnnotations;
using System.ComponentModel.DataAnnotations.Schema;
namespace MealPlan.Api.Models;
[Table("IngredientCategories")]
public class IngredientCategory
{
[Column("Id")]
public int Id { get; set; }
[Column("Code")]
[MaxLength(32)]
public string Code { get; set; } = string.Empty;
[Column("Name")]
[MaxLength(100)]
public string Name { get; set; } = string.Empty;
[Column("SortOrder")]
public int SortOrder { get; set; }
[Column("IsActive")]
public bool IsActive { get; set; } = true;
public ICollection<IngredientNutritionCatalogItem> Items { get; set; } = new List<IngredientNutritionCatalogItem>();
}

View File

@@ -0,0 +1,45 @@
using System.ComponentModel.DataAnnotations;
using System.ComponentModel.DataAnnotations.Schema;
namespace MealPlan.Api.Models;
[Table("IngredientNutritionCatalog")]
public class IngredientNutritionCatalogItem
{
[Column("Id")]
public int Id { get; set; }
[Column("CategoryId")]
public int CategoryId { get; set; }
[Column("Name")]
[MaxLength(255)]
public string Name { get; set; } = string.Empty;
[Column("NamePl")]
[MaxLength(255)]
public string? NamePl { get; set; }
[Column("CaloriesPer100G")]
public int CaloriesPer100G { get; set; }
[Column("ProteinGPer100G")]
public decimal ProteinGPer100G { get; set; }
[Column("FatGPer100G")]
public decimal FatGPer100G { get; set; }
[Column("CarbsGPer100G")]
public decimal CarbsGPer100G { get; set; }
[Column("FiberGPer100G")]
public decimal? FiberGPer100G { get; set; }
[Column("SortOrder")]
public int SortOrder { get; set; }
[Column("IsActive")]
public bool IsActive { get; set; } = true;
public IngredientCategory? Category { get; set; }
}

View File

@@ -0,0 +1,54 @@
using System.ComponentModel.DataAnnotations;
using System.ComponentModel.DataAnnotations.Schema;
namespace MealPlan.Api.Models;
[Table("MealConsumptions")]
public class MealConsumption
{
[Column("Id")]
public int Id { get; set; }
[Column("UserId")]
public Guid UserId { get; set; }
[Column("RecipeId")]
public int? RecipeId { get; set; }
[Column("MealCategory")]
public int MealCategory { get; set; }
[Column("LogDate")]
public DateOnly LogDate { get; set; }
[Column("ConsumedAt")]
public DateTime ConsumedAt { get; set; }
[Column("Name")]
[MaxLength(255)]
public string Name { get; set; } = string.Empty;
[Column("Portions")]
public decimal Portions { get; set; } = 1m;
[Column("Calories")]
public int? Calories { get; set; }
[Column("ProteinG")]
public decimal? ProteinG { get; set; }
[Column("FatG")]
public decimal? FatG { get; set; }
[Column("CarbsG")]
public decimal? CarbsG { get; set; }
[Column("Notes")]
public string? Notes { get; set; }
[Column("CreatedAt")]
public DateTime CreatedAt { get; set; }
public ApplicationUser? User { get; set; }
public Recipe? Recipe { get; set; }
}

View File

@@ -0,0 +1,34 @@
using System.ComponentModel.DataAnnotations.Schema;
namespace MealPlan.Api.Models;
[Table("MealPlanEntries")]
public class MealPlanEntry
{
[Column("Id")]
public int Id { get; set; }
[Column("UserId")]
public Guid UserId { get; set; }
[Column("PlanDate")]
public DateOnly PlanDate { get; set; }
[Column("MealCategory")]
public int MealCategory { get; set; }
[Column("RecipeId")]
public int RecipeId { get; set; }
[Column("Portions")]
public decimal Portions { get; set; } = 1m;
[Column("CreatedAt")]
public DateTime CreatedAt { get; set; }
[Column("UpdatedAt")]
public DateTime? UpdatedAt { get; set; }
public ApplicationUser? User { get; set; }
public Recipe? Recipe { get; set; }
}

View File

@@ -44,6 +44,16 @@ public class Recipe
[Column("IsActive")]
public bool IsActive { get; set; }
[Column("Source")]
[MaxLength(32)]
public string Source { get; set; } = "manual";
[Column("CreatedByUserId")]
public Guid? CreatedByUserId { get; set; }
[Column("IsDraft")]
public bool IsDraft { get; set; }
public ICollection<Ingredient> Ingredients { get; set; } = new List<Ingredient>();
public ICollection<RecipeStep> Steps { get; set; } = new List<RecipeStep>();
}

View File

@@ -0,0 +1,45 @@
using System.ComponentModel.DataAnnotations;
using System.ComponentModel.DataAnnotations.Schema;
namespace MealPlan.Api.Models;
[Table("RecipeGeneratorSessions")]
public class RecipeGeneratorSession
{
[Column("Id")]
public int Id { get; set; }
[Column("UserId")]
public Guid UserId { get; set; }
[Column("Status")]
[MaxLength(32)]
public string Status { get; set; } = RecipeGeneratorStatuses.Draft;
[Column("ConstraintsJson")]
public string ConstraintsJson { get; set; } = "{}";
[Column("DraftJson")]
public string? DraftJson { get; set; }
[Column("SavedRecipeId")]
public int? SavedRecipeId { get; set; }
[Column("GenerationVersion")]
public int GenerationVersion { get; set; } = 1;
[Column("CreatedAt")]
public DateTime CreatedAt { get; set; }
[Column("UpdatedAt")]
public DateTime? UpdatedAt { get; set; }
public Recipe? SavedRecipe { get; set; }
}
public static class RecipeGeneratorStatuses
{
public const string Draft = "draft";
public const string Saved = "saved";
public const string Abandoned = "abandoned";
}

View File

@@ -0,0 +1,26 @@
using System.ComponentModel.DataAnnotations.Schema;
namespace MealPlan.Api.Models;
[Table("RefreshTokens")]
public class RefreshTokenEntity
{
[Column("Token")]
public string Token { get; set; } = string.Empty;
[Column("UserId")]
public Guid UserId { get; set; }
[Column("ExpiresAtUtc")]
public DateTime ExpiresAtUtc { get; set; }
[Column("CreatedAtUtc")]
public DateTime CreatedAtUtc { get; set; }
[Column("IsRevoked")]
public bool IsRevoked { get; set; }
public bool IsActive => !IsRevoked && DateTime.UtcNow < ExpiresAtUtc;
public ApplicationUser? User { get; set; }
}

View File

@@ -0,0 +1,34 @@
using System.ComponentModel.DataAnnotations;
using System.ComponentModel.DataAnnotations.Schema;
namespace MealPlan.Api.Models;
[Table("UserDailyGoals")]
public class UserDailyGoal
{
[Column("UserId")]
public Guid UserId { get; set; }
[Column("CalorieGoal")]
public int? CalorieGoal { get; set; }
[Column("ProteinGoalG")]
public decimal? ProteinGoalG { get; set; }
[Column("FatGoalG")]
public decimal? FatGoalG { get; set; }
[Column("CarbsGoalG")]
public decimal? CarbsGoalG { get; set; }
[Column("WaterGoalMl")]
public int? WaterGoalMl { get; set; }
[Column("CreatedAt")]
public DateTime CreatedAt { get; set; }
[Column("UpdatedAt")]
public DateTime? UpdatedAt { get; set; }
public ApplicationUser? User { get; set; }
}

View File

@@ -0,0 +1,18 @@
using System.ComponentModel.DataAnnotations.Schema;
namespace MealPlan.Api.Models;
[Table("UserFavoriteCatalogItems")]
public class UserFavoriteCatalogItem
{
[Column("UserId")]
public Guid UserId { get; set; }
[Column("CatalogItemId")]
public int CatalogItemId { get; set; }
[Column("CreatedAt")]
public DateTime CreatedAt { get; set; }
public IngredientNutritionCatalogItem? CatalogItem { get; set; }
}

View File

@@ -0,0 +1,18 @@
using System.ComponentModel.DataAnnotations.Schema;
namespace MealPlan.Api.Models;
[Table("UserFavoriteRecipes")]
public class UserFavoriteRecipe
{
[Column("UserId")]
public Guid UserId { get; set; }
[Column("RecipeId")]
public int RecipeId { get; set; }
[Column("CreatedAt")]
public DateTime CreatedAt { get; set; }
public Recipe? Recipe { get; set; }
}