diff --git a/apps/Recipes/Controllers/CatalogController.php b/apps/Recipes/Controllers/CatalogController.php index c12c6fd..288c12e 100644 --- a/apps/Recipes/Controllers/CatalogController.php +++ b/apps/Recipes/Controllers/CatalogController.php @@ -44,6 +44,11 @@ class CatalogController extends BaseController 'is_having' => true ); } + $fields[] = array( + 'name' => 'obj.status', + 'type' => '=', + 'value' => 'publish' + ); $context['recipes_count'] = RecipeModel::count($fields); $context['recipes'] = RecipeModel::filter( diff --git a/apps/Recipes/Controllers/SingleSubmitController.php b/apps/Recipes/Controllers/SingleSubmitController.php index b4f2203..7a4c306 100644 --- a/apps/Recipes/Controllers/SingleSubmitController.php +++ b/apps/Recipes/Controllers/SingleSubmitController.php @@ -2,13 +2,71 @@ namespace Lycoreco\Apps\Recipes\Controllers; +use Exception; +use Lycoreco\Apps\Recipes\Models\RecipeModel; use Lycoreco\Includes\BaseController; use Lycoreco\Apps\Recipes\Models\CategoryModel; +use Lycoreco\Apps\Recipes\Models\IngredientInRecipeModel; +use Lycoreco\Includes\Model\ValidationError; class SingleSubmitController extends BaseController { protected $template_name = APPS_PATH . '/Recipes/Templates/single-submit.php'; + protected function post() + { + $title = $_POST['title'] ?? ''; + $description = $_POST['description'] ?? ''; + $image = $_FILES['image'] ?? null; + $estimated_time = $_POST['est-time'] ?? 0; + $estimated_price = $_POST['est-price'] ?? 0; + $category_id = $_POST['category'] ?? null; + + $ingredient_ids = $_POST['ing-id'] ?? []; + $ingredient_counts = $_POST['ing-count'] ?? []; + + $recipe = new RecipeModel(); + $recipe->field_title = $title; + $recipe->field_instruction = $description; + + if($image) { + $file_url = upload_file($image, RecipeModel::get_table_name() . '/', 'image'); + $recipe->field_image_url = $file_url; + } + $recipe->field_estimated_time = $estimated_time; + $recipe->field_estimated_price = $estimated_price; + $recipe->field_category_id = $category_id; + $recipe->field_author_id = CURRENT_USER->get_id(); + $recipe->field_status = 'pending'; + + try { + $recipe->save(); + } + catch (ValidationError $ex) { + $this->context['error_message'] = $ex->getMessage(); + } + catch (Exception $ex) { + $this->context['error_message'] = "Unexpected error"; + } + + for ($i = 0; $i < count($ingredient_ids); $i++) { + $ing_id = $ingredient_ids[$i]; + $ing_count = (int) $ingredient_counts[$i]; + + if($ing_count <= 0) + continue; + + $relation = new IngredientInRecipeModel(); + $relation->field_ingredient_id = $ing_id; + $relation->field_amount = $ing_count; + $relation->field_recipe_id = $recipe->get_id(); + + $relation->save(); + } + + redirect_to($recipe->get_absolute_url()); + } + public function get_context_data() { $context = parent::get_context_data(); diff --git a/apps/Recipes/Models/CategoryModel.php b/apps/Recipes/Models/CategoryModel.php index 3ed441f..93b0663 100644 --- a/apps/Recipes/Models/CategoryModel.php +++ b/apps/Recipes/Models/CategoryModel.php @@ -25,7 +25,7 @@ class CategoryModel extends BaseModel public static function get_cat_values() { - $cat_list = self::filter(array()); + $cat_list = self::filter(array(), count: 200); $result = array(); foreach($cat_list as $cat) { $result[] = [ $cat->get_id(), $cat->field_name ]; diff --git a/apps/Recipes/Models/RecipeModel.php b/apps/Recipes/Models/RecipeModel.php index 561a84e..0f56be8 100644 --- a/apps/Recipes/Models/RecipeModel.php +++ b/apps/Recipes/Models/RecipeModel.php @@ -1,4 +1,5 @@ 'users us ON us.id = obj.author_id' ], [ - 'field' => [ - - ], + 'field' => [], 'join_table' => 'recipe_ingredients tb2 ON tb2.recipe_id = obj.id' ] ); @@ -57,21 +56,22 @@ class RecipeModel extends BaseModel 'status' => 'string', 'created_at' => 'DateTime' ]; - protected static function get_additional_fields(){ + protected static function get_additional_fields() + { $add_fields = parent::get_additional_fields(); // If user is authorized, we also check product in thw wishlist - if(CURRENT_USER) { + if (CURRENT_USER) { $add_fields = array_merge($add_fields, array( [ "field" => [ - "MAX(CASE WHEN fav.user_id = ". CURRENT_USER->get_id() ." THEN 1 ELSE 0 END) AS is_in_favorite" + "MAX(CASE WHEN fav.user_id = " . CURRENT_USER->get_id() . " THEN 1 ELSE 0 END) AS is_in_favorite" ], "join_table" => "recipe_favorites fav ON fav.recipe_id = obj.id" ] )); } - if(CURRENT_USER) { + if (CURRENT_USER) { $add_fields = array_merge($add_fields, array( [ "field" => [ @@ -106,7 +106,7 @@ class RecipeModel extends BaseModel } public function get_absolute_url() { - return get_permalink('recipes:single', [ $this->get_id() ]); + return get_permalink('recipes:single', [$this->get_id()]); } public function get_price() { @@ -129,7 +129,30 @@ class RecipeModel extends BaseModel return nl2br(trim($this->field_instruction)); } - public function get_time(){ + public function get_time() + { return $this->field_estimated_time . " minutes"; } -} \ No newline at end of file + + public function valid() + { + $errors = []; + + if(mb_strlen($this->field_title) <= 3) + $errors[] = 'Title field must be more than 3 symbols'; + + if($this->field_estimated_time <= 0) + $errors[] = "Estimated time must be more than 0 minutes"; + + if($this->field_estimated_price <= 0) + $errors[] = "Estimated price must be more than 0$"; + + if($this->field_category_id === null) + $errors[] = "Recipe must have category"; + + if(!empty($errors)) + return $errors; + + return true; + } +} diff --git a/apps/Recipes/Templates/single-submit.php b/apps/Recipes/Templates/single-submit.php index 3d8aa2c..429ea6b 100644 --- a/apps/Recipes/Templates/single-submit.php +++ b/apps/Recipes/Templates/single-submit.php @@ -9,12 +9,21 @@ the_header( ] ); ?> +

Submit a Recipe

-
+ + + *
@@ -26,28 +35,34 @@ the_header(
* - - - -
- select 2 goes here -
+ + + + + + + + + + + + + + + + +
NameCount
+
+ + + +
+
+ +
*
@@ -56,12 +71,12 @@ the_header( *
- +
*
-
* @@ -82,6 +97,23 @@ the_header( + + +
diff --git a/assets/css/single-submit.css b/assets/css/single-submit.css new file mode 100644 index 0000000..da18cc0 --- /dev/null +++ b/assets/css/single-submit.css @@ -0,0 +1,23 @@ +.ingredients-table { + overflow: auto; +} +.ingredients-table .input { + margin-bottom: 0; +} +.ingredients-table .input input[type="number"] { + height: auto; + padding: 5px; + width: 100px; +} +#search-ingredient { + position: relative; +} +#search-ingredient input { + border: 0; + padding: 5px 0; + border: 0 !important; + outline: none !important; +} +.custom-select-dropdown .dropdown-item:last-child { + border-top: 0; +} \ No newline at end of file diff --git a/assets/css/style.css b/assets/css/style.css index 7a63042..6f5f87d 100644 --- a/assets/css/style.css +++ b/assets/css/style.css @@ -1063,6 +1063,16 @@ input[type="checkbox"]{ overflow: hidden; border: 1px solid #b3b3b3; border-collapse: separate; + margin-bottom: 20px; +} +.ingredients-table i { + color: #f00; + cursor: pointer; + margin-left: 10px; +} +.ingredients-table td:nth-child(2) { + display: flex; + align-items: center; } .ingredients-table th { @@ -1230,7 +1240,7 @@ label { } .add-ingredient-btn{ - margin-bottom: 25px; + margin-bottom: 0px; } .single-submit-form span{ diff --git a/assets/js/single-submit.js b/assets/js/single-submit.js index 61a3664..843dc46 100644 --- a/assets/js/single-submit.js +++ b/assets/js/single-submit.js @@ -52,4 +52,95 @@ ingredientSubmitBtn.addEventListener('click', async (e) => { ingredientUnit.value = ''; ingModal.classList.add('hidden'); overlay.classList.add('hidden'); +}); + + +const searchIngredientWrapper = document.getElementById('search-ingredient'); +const searchIngInput = searchIngredientWrapper.querySelector('input'); +const searchIngDropdown = searchIngredientWrapper.querySelector('.custom-select-dropdown'); + +const tableIngRows = document.querySelector('.ing-table-rows'); +const ingredientsAdded = new Map(); + +searchIngInput.addEventListener('input', function () { + clearTimeout(searchTimeout); + + searchTimeout = setTimeout(async () => { + let searchValue = this.value.trim(); + + + if (searchValue.length < 3) { + searchIngInput.innerHTML = ''; + searchIngDropdown.hidden = true; + return; + } + + const formData = new FormData(); + formData.append('action', 'search_ingredient'); + formData.append('query', searchValue); + + const response = await fetch('/ajax', { + method: 'POST', + body: formData + }); + + const json = await response.json(); + + if (!response.ok) { + const message = json.error || 'Something went wrong.'; + showToastify(message, 'error'); + return; + } + + const results = json.result; + + searchIngDropdown.innerHTML = ''; + + if (results.length > 0) { + results.forEach(ingredient => { + const ingredientName = `${ingredient.field_name} (${ingredient.field_unit_name})`; + + const option = document.createElement('div'); + option.className = "dropdown-item hover-anim"; + option.textContent = ingredientName; + + option.addEventListener('click', (e) => { + const row = document.createElement('tr'); + + if(ingredientsAdded.get(ingredient.id) != undefined) { + showToastify("This field already exists.", "error"); + return; + } + + row.innerHTML += ` + ${ingredientName} + + +
+ + + `; + row.querySelector(".ing-id").value = ingredient.id; + + + const rowDeleteBtn = row.querySelector('i'); + rowDeleteBtn.addEventListener('click', (e) => { + ingredientsAdded.delete(ingredient.id); + row.remove(); + }); + + ingredientsAdded.set(ingredient.id, ingredientName); + tableIngRows.append(row); + searchIngInput.value = ''; + searchIngDropdown.hidden = true; + }); + + searchIngDropdown.append(option); + }); + searchIngDropdown.hidden = false; + } else { + searchIngDropdown.innerHTML = `
No recipes found
`; + searchIngDropdown.hidden = false; + } + }, 300); }); \ No newline at end of file diff --git a/media/recipes/caesar_686a6c042a944.jpg b/media/recipes/caesar_686a6c042a944.jpg new file mode 100644 index 0000000..a8009aa Binary files /dev/null and b/media/recipes/caesar_686a6c042a944.jpg differ diff --git a/media/recipes/garlic_686a6a714284a.jpg b/media/recipes/garlic_686a6a714284a.jpg new file mode 100644 index 0000000..5b8866e Binary files /dev/null and b/media/recipes/garlic_686a6a714284a.jpg differ