Index.cshtml 7.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170
  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. @* Images disabled on index for performance - only show on details page *@
  80. <div class="card-body">
  81. <h5 class="card-title">@recipe.Title</h5>
  82. <p class="card-text">@recipe.Description</p>
  83. @if (!string.IsNullOrEmpty(recipe.Difficulty))
  84. {
  85. <div class="mb-2">
  86. <span class="badge bg-info">@recipe.Difficulty</span>
  87. </div>
  88. }
  89. @if (!string.IsNullOrEmpty(recipe.Time))
  90. {
  91. <div class="mb-2">
  92. <small class="text-muted">
  93. <i class="fas fa-clock"></i> @recipe.Time
  94. </small>
  95. </div>
  96. }
  97. @if (!string.IsNullOrEmpty(recipe.Url))
  98. {
  99. <div class="mb-2">
  100. <a href="@recipe.Url" target="_blank" class="btn btn-sm btn-outline-primary">
  101. <i class="fas fa-external-link-alt"></i> View Recipe
  102. </a>
  103. </div>
  104. }
  105. </div>
  106. <div class="card-footer">
  107. <div class="btn-group w-100" role="group">
  108. <a href="@Url.Action("Details", new { id = recipe.Id })"
  109. class="btn btn-outline-primary btn-sm">
  110. <i class="fas fa-eye"></i> Details
  111. </a>
  112. <a href="@Url.Action("Edit", new { id = recipe.Id })"
  113. class="btn btn-outline-warning btn-sm">
  114. <i class="fas fa-edit"></i> Edit
  115. </a>
  116. <a href="@Url.Action("Delete", new { id = recipe.Id })"
  117. class="btn btn-outline-danger btn-sm">
  118. <i class="fas fa-trash"></i> Delete
  119. </a>
  120. </div>
  121. </div>
  122. </div>
  123. </div>
  124. }
  125. </div>
  126. }
  127. else
  128. {
  129. <div class="alert alert-info">
  130. <i class="fas fa-info-circle"></i>
  131. @if (!string.IsNullOrEmpty(Model.SearchTerm))
  132. {
  133. <span>No recipes found matching your search criteria.</span>
  134. }
  135. else
  136. {
  137. <span>No recipes found in the database.</span>
  138. }
  139. </div>
  140. }
  141. </div>
  142. </div>
  143. </div>
  144. @section Scripts {
  145. <script>
  146. // Auto-submit search after typing (debounced)
  147. let searchTimeout;
  148. document.getElementById('searchTerm').addEventListener('input', function () {
  149. clearTimeout(searchTimeout);
  150. searchTimeout = setTimeout(() => {
  151. if (this.value.length > 2 || this.value.length === 0) {
  152. this.form.submit();
  153. }
  154. }, 500);
  155. });
  156. </script>
  157. }