Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Issue #9 Recipe Details 🎫 #23

Merged
merged 13 commits into from
Dec 21, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
51 changes: 51 additions & 0 deletions app/controllers/recipe_foods_controller.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,51 @@
class RecipeFoodsController < ApplicationController
def index
# match the recipe id with the recipe_foods recipe_id
@recipe_foods = RecipeFood.where(recipe_id: params[:recipe_id])
@recipe = Recipe.find(params[:recipe_id])
@food = Food.where(food_id: params[:food_id])
end

def new
@recipe_food = RecipeFood.new
@recipe_id = params[:recipe_id]
@foods = Food.all
end

def create
@recipe_food = RecipeFood.new(recipe_food_params)
if @recipe_food.save
flash[:success] = 'Ingredient was successfully added.'
redirect_to recipe_path(id: @recipe_food.recipe_id)
else
render :new, alert: 'Failed to add ingredient'
end
end

def update
respond_to do |format|
if @recipe_food.update(recipe_food_params)
flash[:success] = 'Recipe was successfully updated.'
format.html { redirect_to recipe_food_path(@recipe_food) }
else
flash[:danger] = 'Recipe was not updated.'
format.html { render :edit, status: :unprocessable_entity }
end
end
end

def destroy
@recipe_id = params[:recipe_id]
@recipe_food = RecipeFood.find(params[:id])
return unless @recipe_food.destroy

flash[:success] = 'Ingredient was successfully deleted.'
redirect_to recipe_path(id: @recipe_food.recipe_id)
end

private

def recipe_food_params
params.require(:recipe_food).permit(:quantity, :recipe_id, :food_id)
end
end
12 changes: 10 additions & 2 deletions app/controllers/recipes_controller.rb
Original file line number Diff line number Diff line change
@@ -1,4 +1,7 @@
class RecipesController < ApplicationController
# uncomment the below line to enable authentication on the public recipes page
# public pages will be only visible across loggd in user and not to the public
# before_action :authenticate_user!
before_action :set_recipe, only: %i[show edit update destroy]

# GET /recipes or /recipes.json
Expand All @@ -11,7 +14,10 @@ def public_index
end

# GET /recipes/1 or /recipes/1.json
def show; end
def show
@recipe = Recipe.includes(:recipe_foods).find(params[:id])
@recipe_food = @recipe.recipe_foods.includes(:food)
end

# GET /recipes/new
def new
Expand Down Expand Up @@ -53,9 +59,11 @@ def update
# DELETE /recipes/1 or /recipes/1.json
def destroy
@recipe.destroy!
# Delete associated recipe_foods
@recipe.recipe_foods.destroy_all

respond_to do |format|
format.html { redirect_to recipes_url, notice: 'Recipe was successfully destroyed.' }
format.html { redirect_to recipes_url, notice: 'Recipe was successfully deleted.' }
format.json { head :no_content }
end
end
Expand Down
2 changes: 1 addition & 1 deletion app/models/food.rb
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
class Food < ApplicationRecord
belongs_to :user
has_many :recipe_foods
has_many :recipe_foods, dependent: :destroy
has_many :recipes, through: :recipe_foods

validates :name, presence: true
Expand Down
2 changes: 1 addition & 1 deletion app/models/recipe.rb
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
class Recipe < ApplicationRecord
belongs_to :user
has_many :recipe_foods
has_many :recipe_foods, dependent: :destroy
has_many :foods, through: :recipe_foods

validates :name, presence: true
Expand Down
4 changes: 4 additions & 0 deletions app/models/recipe_food.rb
Original file line number Diff line number Diff line change
@@ -1,4 +1,8 @@
class RecipeFood < ApplicationRecord
belongs_to :recipe
belongs_to :food

validates :quantity, presence: true, numericality: { greater_than: 0 }
validates :recipe_id, presence: true
validates :food_id, presence: true
end
25 changes: 25 additions & 0 deletions app/views/recipe_foods/new.html.erb
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
<div style="max-width: 400px; margin: 0 auto;" class="mb-3">
<h1 id="form-title" class="text-center text-muted m-2">Add new Food to Recipe</h1>
<%= form_with(model: [@recipe_food], url: recipe_foods_path) do |form| %>
<div class="justify-content-center gap-2">
<div class="col-md-4">
<%= form.hidden_field :recipe_id, value: @recipe_id %>
</div>
<div class="col-md-7">
<div class="mb-3">
<%= form.label :name, class: "form-label" %>
<%= form.collection_select(:food_id, Food.all, :id, :name, { prompt: "Select a Food" }, { class: "form-select" }) %>
</div>
</div>
<div class="col-md-5">
<div class="mb-3">
<%= form.label :quantity, class: "form-label" %>
<%= form.number_field :quantity, autofocus: true, autocomplete: "quantity", placeholder: "Quantity", class: "form-control" %>
</div>
</div>
<div class="col-md-4 text-center">
<%= form.submit 'Add Food to Recipe', class: "btn btn-primary mt-3" %>
</div>
</div>
<% end %>
</div>
69 changes: 40 additions & 29 deletions app/views/recipes/_recipe.html.erb
Original file line number Diff line number Diff line change
@@ -1,32 +1,43 @@
<div id="<%= dom_id recipe %>">
<p>
<strong>Name:</strong>
<%= recipe.name %>
</p>
<div id="<%= dom_id recipe %>" class="m-2">
<h2 class="text-center text-muted">
<%= recipe.name %>
</h2>
<div class="d-flex justify-content-around p-4"
style="max-width: 800px; margin: 0 auto;">
<div>
<p>
<strong>Preparation time:</strong>
<%= recipe.preparation_time %>
</p>

<p>
<strong>Preparation time:</strong>
<%= recipe.preparation_time %>
</p>

<p>
<strong>Cooking time:</strong>
<%= recipe.cooking_time %>
</p>

<p>
<strong>Description:</strong>
<%= recipe.description %>
</p>

<p>
<strong>Public:</strong>
<%= recipe.public %>
</p>

<p>
<strong>User:</strong>
<%= recipe.user_id %>
</p>
<p>
<strong>Cooking time:</strong>
<%= recipe.cooking_time %>
</p>

<p>
<details>
<summary>Description:</summary>
<p> <%= recipe.description %> </p>
</details>
</p>
</div>

<div>
<% if recipe.user == current_user %>
<%= form_with(model: recipe) do |form| %>
<div class="custom-control custom-switch">
<%= form.check_box :public, class: "custom-control-input",
id: "publicswitch", onchange: "submitForm(this);" %>
<label class="custom-control-label" for="publicswitch"> Public: </label>
</div>
<% end %>
<% end %>
<script>
function submitForm(checkbox) {
checkbox.form.submit();
}
</script>
</div>
</div>
</div>
58 changes: 52 additions & 6 deletions app/views/recipes/show.html.erb
Original file line number Diff line number Diff line change
@@ -1,10 +1,56 @@
<p style="color: green"><%= notice %></p>

<div id="recipe-detail">
<%= render @recipe %>

<div>
<%= link_to "Edit this recipe", edit_recipe_path(@recipe) %> |
<%= link_to "Back to recipes", recipes_path %>
<div class="d-flex justify-content-around p-3 m-1">
<button>Generate Shopping list</button>
<% if @recipe.user == current_user %>
<%= link_to "Add ingredient", new_recipe_food_path(recipe_id: @recipe.id), class: "btn btn-outline-success" %>
<% end %>
</div>

<div class="text-center m-1">
<hr>

<div id="foods">
<% if @recipe_food.empty? %>
<div class="alert alert-info text-center">
<p class="mb-0">There are no ingredients for this recipe</p>
</div>
<% else %>
<table class="table table-hover table-striped border">
<thead>
<tr>
<th>Food</th>
<th>Quantity</th>
<th>Value</th>
<th>Actions</th>
</tr>
</thead>

<tbody>
<% @recipe_food.each do |ingredient| %>
<tr>
<td><%= ingredient.food.name %></td>
<td><%= "#{ingredient.quantity}#{ingredient.food.measurement_unit}" %></td>
<td><%= "$#{ingredient.food.price*ingredient.quantity}" %></td>
<% if @recipe.user == current_user %>
<td><%= link_to "Modify", edit_recipe_food_path(ingredient), class: "btn btn-ountline-danger", method: :update, data: { "turbo-method": :update } %></td>
<td><%= link_to "Delete", ingredient, class: "btn btn-ountline-danger", method: :delete, data: { "turbo-method": :delete } %></td>
<% else %>
<td>No actions</td>
<% end %>
</tr>
<% end %>
</tbody>
</table>
<% end %>
</div>

<%= button_to "Destroy this recipe", @recipe, method: :delete %>
<% if @recipe.user.id == current_user.id %>
<%= link_to "Edit this recipe", edit_recipe_path(@recipe),
class: "btn btn-outline-warning mb-1" %>
<%= button_to "Delete recipe", @recipe, method: :delete,
class: "btn btn-outline-danger" %>
<% end %>
</div>
</div>
1 change: 1 addition & 0 deletions config/routes.rb
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
Rails.application.routes.draw do
resources :foods
resources :recipes
resources :recipe_foods
devise_for :users
# Define your application routes per the DSL in https://guides.rubyonrails.org/routing.html

Expand Down
45 changes: 45 additions & 0 deletions spec/controllers/recipe_foods_controller_spec.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
require 'rails_helper'

RSpec.describe RecipeFoodsController, type: :controller do
include Devise::Test::ControllerHelpers
let(:user) { create(:user) }
let(:recipe) { create(:recipe, user:) }
let(:food) { create(:food) }
let(:valid_attributes) { attributes_for(:recipe_food, recipe_id: recipe.id, food_id: food.id) }
let(:invalid_attributes) { attributes_for(:recipe_food, quantity: nil, recipe_id: recipe.id, food_id: food.id) }

before do
sign_in user
end

describe 'GET #new' do
it 'returns a success response' do
recipe_food = RecipeFood.create! valid_attributes
get :new, params: { recipe_id: recipe_food.recipe_id }
expect(response).to be_successful
end
end

describe 'POST #create' do
context 'with valid params' do
it 'creates a new RecipeFood' do
expect do
post :create, params: { recipe_food: valid_attributes }
end.to change(RecipeFood, :count).by(1)
end

it 'redirects to the recipe show page' do
post :create, params: { recipe_food: valid_attributes }
expect(response).to redirect_to(recipe_path(id: valid_attributes[:recipe_id]))
end
end

context 'with invalid params' do
it 'does not create a new RecipeFood' do
expect do
post :create, params: { recipe_food: invalid_attributes }
end.to change(RecipeFood, :count).by(0)
end
end
end
end
7 changes: 7 additions & 0 deletions spec/factories/recipe_foods.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
FactoryBot.define do
factory :recipe_food do
food { create(:food) }
recipe { create(:recipe) }
quantity { 1 }
end
end
46 changes: 46 additions & 0 deletions spec/models/recipe_foods_spec.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,46 @@
require 'rails_helper'
RSpec.describe RecipeFood, type: :model do
let(:recipe_food) { build(:recipe_food) } # ! non persisting object. It's not saved in the database.

describe 'validations' do
context 'object itself' do
it 'should be valid' do
expect(recipe_food).to be_valid
end
end

context 'quantity validations' do
it 'should be valid' do
expect(recipe_food.quantity).to be_present
expect(recipe_food.quantity).to be > 0
end

it 'should be invalid' do
recipe_food.quantity = nil
expect(recipe_food).to_not be_valid
end
end

context 'recipe_id validations' do
it 'should be valid' do
expect(recipe_food.recipe_id).to be_present
end

it 'should be invalid' do
recipe_food.recipe_id = nil
expect(recipe_food).to_not be_valid
end
end

context 'food_id validations' do
it 'should be valid' do
expect(recipe_food.food_id).to be_present
end

it 'should be invalid' do
recipe_food.food_id = nil
expect(recipe_food).to_not be_valid
end
end
end
end
Loading
Loading