Index.cshtml 8.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177
  1. @model Recepie.ViewModels.RecipeIndexViewModel
  2. @{
  3. ViewData["Title"] = "Recipe Search";
  4. }
  5. <div class="container-fluid mt-4">
  6. <div class="row">
  7. <div class="col-md-3">
  8. <!-- Search Panel -->
  9. <div class="card">
  10. <div class="card-header">
  11. <h5 class="mb-0">
  12. <i class="fas fa-search"></i> Search Recipes
  13. </h5>
  14. </div>
  15. <div class="card-body">
  16. <form method="get" asp-action="Index">
  17. <!-- Search Term -->
  18. <div class="mb-3">
  19. <label for="searchTerm" class="form-label">Search</label>
  20. <input type="text" class="form-control" id="searchTerm" name="searchTerm"
  21. value="@Model.SearchTerm" placeholder="Search title or description...">
  22. </div>
  23. <!-- Include Ingredients -->
  24. <div class="mb-3">
  25. <label for="includeIngredients" class="form-label">
  26. <i class="fas fa-plus text-success"></i> Must Have Ingredients
  27. </label>
  28. <input type="text" class="form-control" id="includeIngredients" name="includeIngredients"
  29. value="@Model.IncludeIngredients" placeholder="chicken, tomato, onion...">
  30. <div class="form-text">Comma-separated list</div>
  31. </div>
  32. <!-- Exclude Ingredients -->
  33. <div class="mb-3">
  34. <label for="excludeIngredients" class="form-label">
  35. <i class="fas fa-minus text-danger"></i> Exclude Ingredients
  36. </label>
  37. <input type="text" class="form-control" id="excludeIngredients" name="excludeIngredients"
  38. value="@Model.ExcludeIngredients" placeholder="nuts, dairy, gluten...">
  39. <div class="form-text">Comma-separated list</div>
  40. </div>
  41. <button type="submit" class="btn btn-primary w-100">
  42. <i class="fas fa-search"></i> Search & Filter
  43. </button>
  44. @if (!string.IsNullOrEmpty(Model.SearchTerm) || !string.IsNullOrEmpty(Model.IncludeIngredients)
  45. || !string.IsNullOrEmpty(Model.ExcludeIngredients))
  46. {
  47. <a href="@Url.Action("Index")" class="btn btn-secondary w-100 mt-2">
  48. <i class="fas fa-times"></i> Clear All Filters
  49. </a>
  50. }
  51. </form>
  52. </div>
  53. </div>
  54. <!-- Stats Panel -->
  55. <div class="card mt-3">
  56. <div class="card-body">
  57. <h6 class="card-title">Results</h6>
  58. <p class="card-text">
  59. Found <strong>@Model.Recipes.Count</strong> recipes
  60. </p>
  61. </div>
  62. </div>
  63. </div>
  64. <div class="col-md-9">
  65. <!-- Recipe Results -->
  66. <div class="d-flex justify-content-between align-items-center mb-3">
  67. <h2>Recipes</h2>
  68. <a href="@Url.Action("Create")" class="btn btn-success">
  69. <i class="fas fa-plus"></i> Add New Recipe
  70. </a>
  71. </div>
  72. @if (Model.Recipes.Any())
  73. {
  74. <div class="row">
  75. @foreach (var recipe in Model.Recipes)
  76. {
  77. <div class="col-md-6 col-lg-4 mb-4">
  78. <div class="card h-100">
  79. @* Small thumbnail image with error handling *@
  80. <img src="@Url.Action("GetImage", "Recipe", new { recipeId = recipe.Id })" class="card-img-top"
  81. alt="@recipe.Title" style="height: 200px; object-fit: cover; background-color: #f8f9fa;"
  82. onerror="this.style.display='none'; this.nextElementSibling.style.display='block';" />
  83. <div class="card-img-placeholder text-center p-3"
  84. style="display: none; height: 200px; background-color: #f8f9fa; align-items: center; justify-content: center;">
  85. <i class="fas fa-utensils fa-3x text-muted"></i>
  86. </div>
  87. <div class="card-body">
  88. <h5 class="card-title">@recipe.Title</h5>
  89. <p class="card-text">@recipe.Description</p>
  90. @if (!string.IsNullOrEmpty(recipe.Difficulty))
  91. {
  92. <div class="mb-2">
  93. <span class="badge bg-info">@recipe.Difficulty</span>
  94. </div>
  95. }
  96. @if (!string.IsNullOrEmpty(recipe.Time))
  97. {
  98. <div class="mb-2">
  99. <small class="text-muted">
  100. <i class="fas fa-clock"></i> @recipe.Time
  101. </small>
  102. </div>
  103. }
  104. @if (!string.IsNullOrEmpty(recipe.Url))
  105. {
  106. <div class="mb-2">
  107. <a href="@recipe.Url" target="_blank" class="btn btn-sm btn-outline-primary">
  108. <i class="fas fa-external-link-alt"></i> View Recipe
  109. </a>
  110. </div>
  111. }
  112. </div>
  113. <div class="card-footer">
  114. <div class="btn-group w-100" role="group">
  115. <a href="@Url.Action("Details", new { id = recipe.Id })"
  116. class="btn btn-outline-primary btn-sm">
  117. <i class="fas fa-eye"></i> Details
  118. </a>
  119. <a href="@Url.Action("Edit", new { id = recipe.Id })"
  120. class="btn btn-outline-warning btn-sm">
  121. <i class="fas fa-edit"></i> Edit
  122. </a>
  123. <a href="@Url.Action("Delete", new { id = recipe.Id })"
  124. class="btn btn-outline-danger btn-sm">
  125. <i class="fas fa-trash"></i> Delete
  126. </a>
  127. </div>
  128. </div>
  129. </div>
  130. </div>
  131. }
  132. </div>
  133. }
  134. else
  135. {
  136. <div class="alert alert-info">
  137. <i class="fas fa-info-circle"></i>
  138. @if (!string.IsNullOrEmpty(Model.SearchTerm))
  139. {
  140. <span>No recipes found matching your search criteria.</span>
  141. }
  142. else
  143. {
  144. <span>No recipes found in the database.</span>
  145. }
  146. </div>
  147. }
  148. </div>
  149. </div>
  150. </div>
  151. @section Scripts {
  152. <script>
  153. // Auto-submit search after typing (debounced)
  154. let searchTimeout;
  155. document.getElementById('searchTerm').addEventListener('input', function () {
  156. clearTimeout(searchTimeout);
  157. searchTimeout = setTimeout(() => {
  158. if (this.value.length > 2 || this.value.length === 0) {
  159. this.form.submit();
  160. }
  161. }, 500);
  162. });
  163. </script>
  164. }