diff --git a/app/.idea/codeStyles/Project.xml b/app/.idea/codeStyles/Project.xml index 7643783..ab75be2 100644 --- a/app/.idea/codeStyles/Project.xml +++ b/app/.idea/codeStyles/Project.xml @@ -1,6 +1,7 @@ + diff --git a/app/app/src/main/java/tech/mercantec/easyeat/helpers/auth.kt b/app/app/src/main/java/tech/mercantec/easyeat/helpers/auth.kt index 01849c9..672be6e 100644 --- a/app/app/src/main/java/tech/mercantec/easyeat/helpers/auth.kt +++ b/app/app/src/main/java/tech/mercantec/easyeat/helpers/auth.kt @@ -3,7 +3,7 @@ package tech.mercantec.easyeat.helpers import android.content.Context import android.util.Log import kotlinx.serialization.Serializable - +import tech.mercantec.easyeat.models.Recipe @Serializable data class LoginRequest(val emailUsr: String, val password: String) @@ -109,3 +109,12 @@ fun changePassword(ctx: Context, oldPassword: String, newPassword: String) { return requestJson(ctx, "PUT", "/api/User/change-password", request) } +@Serializable +data class CreateRecipeRequest(val recipe: Recipe) + +fun createRecipe(ctx: Context, recipe: Recipe) { + val request = CreateRecipeRequest(recipe) + + return requestJson(ctx, "POST", "/api/recipe/create", request) + +} diff --git a/app/app/src/main/java/tech/mercantec/easyeat/helpers/shopping_list.kt b/app/app/src/main/java/tech/mercantec/easyeat/helpers/shopping_list.kt new file mode 100644 index 0000000..aa42d3e --- /dev/null +++ b/app/app/src/main/java/tech/mercantec/easyeat/helpers/shopping_list.kt @@ -0,0 +1,22 @@ +package tech.mercantec.easyeat.helpers + +import android.content.Context +import kotlinx.serialization.Serializable +import tech.mercantec.easyeat.models.ShoppingListItem + +fun getShoppingList(ctx: Context): Array { + return requestJson>(ctx, "GET", "/api/ShoppingList/get", null) +} + +@Serializable +data class AddShoppingItemRequest(val name: String, val amount: Double?, val unit: String?, val checked: Boolean) + +fun addShoppingItem(ctx: Context, name: String, amount: Double?, unit: String?) { + val request = AddShoppingItemRequest(name, amount, unit, false) + + requestJson(ctx, "POST", "/api/ShoppingList/add", request) +} + +fun toggleShoppingItemChecked(ctx: Context, item: ShoppingListItem) { + requestJson(ctx, "PUT", "/api/ShoppingList/check?itemId=${item.id}", null) +} diff --git a/app/app/src/main/java/tech/mercantec/easyeat/models/Ingredient.kt b/app/app/src/main/java/tech/mercantec/easyeat/models/Ingredient.kt deleted file mode 100644 index 7413734..0000000 --- a/app/app/src/main/java/tech/mercantec/easyeat/models/Ingredient.kt +++ /dev/null @@ -1,7 +0,0 @@ -package tech.mercantec.easyeat.models - -data class Ingredient( - val Amount: Int, - val Unit: String, - val Element: String -) \ No newline at end of file diff --git a/app/app/src/main/java/tech/mercantec/easyeat/models/ShoppingListItem.kt b/app/app/src/main/java/tech/mercantec/easyeat/models/ShoppingListItem.kt new file mode 100644 index 0000000..a97a6a7 --- /dev/null +++ b/app/app/src/main/java/tech/mercantec/easyeat/models/ShoppingListItem.kt @@ -0,0 +1,6 @@ +package tech.mercantec.easyeat.models + +import kotlinx.serialization.Serializable + +@Serializable +data class ShoppingListItem(val id: Int, var name: String, var amount: Double?, var unit: String?, var checked: Boolean) diff --git a/app/app/src/main/java/tech/mercantec/easyeat/models/recipe.kt b/app/app/src/main/java/tech/mercantec/easyeat/models/recipe.kt new file mode 100644 index 0000000..1215baa --- /dev/null +++ b/app/app/src/main/java/tech/mercantec/easyeat/models/recipe.kt @@ -0,0 +1,24 @@ +package tech.mercantec.easyeat.models + +import kotlinx.serialization.Serializable + +@Serializable +data class Recipe( + val name: String, + val description: String, + val directions: List, + val ingredients: List +) + +@Serializable +data class Direction( + val instructions: String +) + + +@Serializable +data class Ingredient( + val amount: Double?, + val unit: String?, + val name: String +) diff --git a/app/app/src/main/java/tech/mercantec/easyeat/ui/dishes/CreateDishActivity.kt b/app/app/src/main/java/tech/mercantec/easyeat/ui/dishes/CreateDishActivity.kt index 52666df..a6d0463 100644 --- a/app/app/src/main/java/tech/mercantec/easyeat/ui/dishes/CreateDishActivity.kt +++ b/app/app/src/main/java/tech/mercantec/easyeat/ui/dishes/CreateDishActivity.kt @@ -1,5 +1,7 @@ package tech.mercantec.easyeat.ui.dishes +import android.app.ProgressDialog +import android.content.Context import android.os.Bundle import android.util.Log import android.view.LayoutInflater @@ -9,9 +11,18 @@ import android.widget.EditText import android.widget.ImageButton import android.widget.LinearLayout import android.widget.Spinner +import android.widget.Toast import androidx.appcompat.app.AppCompatActivity +import androidx.core.content.ContentProviderCompat.requireContext import tech.mercantec.easyeat.R +import tech.mercantec.easyeat.helpers.ApiRequestException +import tech.mercantec.easyeat.helpers.changePassword +import tech.mercantec.easyeat.helpers.createRecipe +import tech.mercantec.easyeat.helpers.request +import tech.mercantec.easyeat.models.Direction import tech.mercantec.easyeat.models.Ingredient +import tech.mercantec.easyeat.models.Recipe +import kotlin.concurrent.thread class CreateDishActivity : AppCompatActivity() { @@ -35,16 +46,61 @@ class CreateDishActivity : AppCompatActivity() { val saveButton: Button = findViewById(R.id.saveDishButton) saveButton.setOnClickListener { val ingredientList = collectIngredients() - - val name = findViewById(R.id.dishName).text.toString().trim() - val description = findViewById(R.id.dishDescription).text.toString().trim() val instructions = findViewById(R.id.instructions).text.toString().trim() - // Debug/log example - for (ingredient in ingredientList) { - Log.d("INGREDIENT", "Name: ${ingredient.Element}, Amount: ${ingredient.Amount}, Unit: ${ingredient.Unit}") + val directions: List = instructions + .split("\n") + .map { line -> line.trim() } + .filter { it.isNotEmpty() } + .map { line -> Direction(instructions = line) } + + val recipe = Recipe( + name = findViewById(R.id.dishName).text.toString().trim(), + description = findViewById(R.id.dishDescription).text.toString().trim(), + directions = directions, + ingredients = ingredientList + ) + + Log.i("recipe name:", recipe.name) + Log.i("recipe name:", recipe.description) + for (x in recipe.directions) { + Log.i("recipe name:", x.instructions) + } + for(x in recipe.ingredients){ + Log.i("recipe name:", x.name) + Log.i("recipe name:", x.amount.toString()) + Log.i("recipe name:", x.unit.toString()) + } + val progressDialog = ProgressDialog(this) + progressDialog.setMessage("Loading...") + progressDialog.show() + thread { + try { + createRecipe(this, recipe) + } catch (e: ApiRequestException) { + runOnUiThread { + Toast.makeText(this, e.message, Toast.LENGTH_LONG).show() + } + + return@thread + } finally { + runOnUiThread { + progressDialog.hide() + } + } + + runOnUiThread { + Toast.makeText(this, "Password changed successfully", Toast.LENGTH_LONG).show() + } + + finish() } } + + + + + } private fun addIngredientRow() { @@ -84,7 +140,7 @@ class CreateDishActivity : AppCompatActivity() { // Optional: Only add non-empty rows if (element.isNotEmpty() && amount.isNotEmpty()) { - ingredients.add(Ingredient(Element = element, Amount = amount.toInt(), Unit = unit)) + ingredients.add(Ingredient(name = element, amount = amount.toDouble(), unit = unit)) } } diff --git a/app/app/src/main/java/tech/mercantec/easyeat/ui/shopping_list/ShoppingItemAdapter.kt b/app/app/src/main/java/tech/mercantec/easyeat/ui/shopping_list/ShoppingItemAdapter.kt new file mode 100644 index 0000000..be952f2 --- /dev/null +++ b/app/app/src/main/java/tech/mercantec/easyeat/ui/shopping_list/ShoppingItemAdapter.kt @@ -0,0 +1,60 @@ +package tech.mercantec.easyeat.ui.shopping_list + +import android.content.Context +import android.graphics.Paint +import android.icu.text.DecimalFormat +import android.util.TypedValue +import android.view.LayoutInflater +import android.view.View +import android.view.ViewGroup +import android.widget.ArrayAdapter +import android.widget.ImageView +import android.widget.TextView +import androidx.core.content.ContextCompat +import tech.mercantec.easyeat.R +import tech.mercantec.easyeat.models.ShoppingListItem + +class ShoppingItemAdapter(context: Context, items: ArrayList) + : ArrayAdapter(context, R.layout.shopping_list_item, items) { + + override fun getView(position: Int, convertView: View?, parent: ViewGroup): View { + val item = getItem(position) + val view = convertView ?: LayoutInflater.from(context).inflate(R.layout.shopping_list_item, parent, false) + + item?.let { item -> + val checkmarkView = view.findViewById(R.id.checkmark) + val amountView = view.findViewById(R.id.amount) + val unitView = view.findViewById(R.id.unit) + val nameView = view.findViewById(R.id.name) + val textViews = listOf(amountView, unitView, nameView) + + amountView.text = DecimalFormat("#.##").format(item.amount) + unitView.text = item.unit + nameView.text = item.name + + if (item.checked) { + textViews.forEach { + it.paintFlags = it.paintFlags or Paint.STRIKE_THRU_TEXT_FLAG + + val color = TypedValue() + context.theme.resolveAttribute(R.attr.colorDisabled, color, true) + it.setTextColor(ContextCompat.getColor(context, color.resourceId)) + + checkmarkView.visibility = View.VISIBLE + } + } else { + textViews.forEach { + it.paintFlags = it.paintFlags and Paint.STRIKE_THRU_TEXT_FLAG.inv() + + val color = TypedValue() + context.theme.resolveAttribute(android.R.attr.textColorSecondary, color, true) + it.setTextColor(ContextCompat.getColor(context, color.resourceId)) + + checkmarkView.visibility = View.INVISIBLE + } + } + } + + return view + } +} diff --git a/app/app/src/main/java/tech/mercantec/easyeat/ui/shopping_list/ShoppingListFragment.kt b/app/app/src/main/java/tech/mercantec/easyeat/ui/shopping_list/ShoppingListFragment.kt index fef40ae..b1e9dc3 100644 --- a/app/app/src/main/java/tech/mercantec/easyeat/ui/shopping_list/ShoppingListFragment.kt +++ b/app/app/src/main/java/tech/mercantec/easyeat/ui/shopping_list/ShoppingListFragment.kt @@ -1,15 +1,26 @@ package tech.mercantec.easyeat.ui.shopping_list import android.app.AlertDialog +import android.app.Dialog import android.os.Bundle import android.view.LayoutInflater import android.view.View import android.view.ViewGroup import android.widget.ArrayAdapter +import android.widget.EditText import android.widget.Spinner +import android.widget.Toast import androidx.fragment.app.Fragment import tech.mercantec.easyeat.R import tech.mercantec.easyeat.databinding.FragmentShoppingListBinding +import tech.mercantec.easyeat.helpers.ApiRequestException +import tech.mercantec.easyeat.helpers.addShoppingItem +import tech.mercantec.easyeat.helpers.getShoppingList +import tech.mercantec.easyeat.helpers.toggleShoppingItemChecked +import tech.mercantec.easyeat.models.Dish +import tech.mercantec.easyeat.models.ShoppingListItem +import java.util.ArrayList +import kotlin.concurrent.thread class ShoppingListFragment : Fragment() { @@ -27,17 +38,73 @@ class ShoppingListFragment : Fragment() { _binding = FragmentShoppingListBinding.inflate(inflater, container, false) val root: View = binding.root + thread { + val items: Array + try { + items = getShoppingList(requireContext()) + } catch (e: ApiRequestException) { + activity?.runOnUiThread { + Toast.makeText(context, e.message, Toast.LENGTH_LONG).show() + } + + return@thread + } + + activity?.runOnUiThread { + binding.shoppingList.adapter = ShoppingItemAdapter(requireContext(), ArrayList(items.toMutableList())) + } + } + + binding.shoppingList.setOnItemClickListener { parent, view, position, id -> + val item = parent.getItemAtPosition(position) as ShoppingListItem + item.checked = !item.checked + + thread { + try { + toggleShoppingItemChecked(requireContext(), item) + } catch (e: ApiRequestException) { + Toast.makeText(requireContext(), e.message, Toast.LENGTH_LONG).show() + } + } + + val adapter = parent.adapter as ShoppingItemAdapter + + adapter.remove(item) + adapter.insert(item, position) + } + binding.addToShoppingList.setOnClickListener { val view = requireActivity().layoutInflater.inflate(R.layout.dialog_add_to_shopping_list, null) val dialog = AlertDialog.Builder(activity) .setView(view) - .setPositiveButton(R.string.add_label, { dialog, id -> + .setPositiveButton(R.string.add_label) { dialog, id -> + val dialog = dialog as AlertDialog - }) - .setNegativeButton(R.string.cancel_label, { dialog, id -> + val amount = view.findViewById(R.id.amount).text.toString().toDouble() + val unit = view.findViewById(R.id.unit_selector).selectedItem.toString().ifEmpty { null } + val name = view.findViewById(R.id.name).text.toString() + + dialog.getButton(AlertDialog.BUTTON_POSITIVE).isEnabled = false + dialog.getButton(AlertDialog.BUTTON_NEGATIVE).isEnabled = false + + thread { + try { + addShoppingItem(requireContext(), name, amount, unit) + } catch (e: ApiRequestException) { + activity?.runOnUiThread { + Toast.makeText(context, e.message, Toast.LENGTH_LONG).show() + } + } finally { + activity?.runOnUiThread { + dialog.dismiss() + } + } + } + } + .setNegativeButton(R.string.cancel_label) { dialog, _ -> dialog.cancel() - }) + } .create() dialog.show() diff --git a/app/app/src/main/res/drawable/ic_check_24px.xml b/app/app/src/main/res/drawable/ic_check_24px.xml new file mode 100644 index 0000000..280f0bd --- /dev/null +++ b/app/app/src/main/res/drawable/ic_check_24px.xml @@ -0,0 +1,10 @@ + + + diff --git a/app/app/src/main/res/layout/activity_main.xml b/app/app/src/main/res/layout/activity_main.xml index 37665d9..e338c8e 100644 --- a/app/app/src/main/res/layout/activity_main.xml +++ b/app/app/src/main/res/layout/activity_main.xml @@ -2,11 +2,9 @@ + android:layout_height="match_parent"> diff --git a/app/app/src/main/res/layout/shopping_list_item.xml b/app/app/src/main/res/layout/shopping_list_item.xml new file mode 100644 index 0000000..f71e2d3 --- /dev/null +++ b/app/app/src/main/res/layout/shopping_list_item.xml @@ -0,0 +1,45 @@ + + + + + + + + + + + + diff --git a/app/app/src/main/res/values-night/themes.xml b/app/app/src/main/res/values-night/themes.xml index a8f2ade..91d3f75 100644 --- a/app/app/src/main/res/values-night/themes.xml +++ b/app/app/src/main/res/values-night/themes.xml @@ -4,6 +4,7 @@ @color/dark_cyan @color/white @color/dark_gray + @color/dark_gray @color/dark_cyan ?attr/colorPrimaryVariant @color/black diff --git a/app/app/src/main/res/values/attrs.xml b/app/app/src/main/res/values/attrs.xml new file mode 100644 index 0000000..d82cf71 --- /dev/null +++ b/app/app/src/main/res/values/attrs.xml @@ -0,0 +1,6 @@ + + + + + + diff --git a/app/app/src/main/res/values/colors.xml b/app/app/src/main/res/values/colors.xml index 9c94335..4af2f08 100644 --- a/app/app/src/main/res/values/colors.xml +++ b/app/app/src/main/res/values/colors.xml @@ -7,4 +7,5 @@ #FF242424 #FFF9F9F9 #D62D2D + #4CAF50 diff --git a/app/app/src/main/res/values/strings.xml b/app/app/src/main/res/values/strings.xml index e134a57..741c1d1 100644 --- a/app/app/src/main/res/values/strings.xml +++ b/app/app/src/main/res/values/strings.xml @@ -38,7 +38,9 @@ Beef Change password Log out + Checked + g kg ml diff --git a/app/app/src/main/res/values/themes.xml b/app/app/src/main/res/values/themes.xml index 30afe6a..eff4e5d 100644 --- a/app/app/src/main/res/values/themes.xml +++ b/app/app/src/main/res/values/themes.xml @@ -4,6 +4,7 @@ @color/dark_cyan @color/white @color/dark_gray + @color/gray ?attr/colorPrimaryVariant @color/white diff --git a/backend/API/Controllers/RecipeController.cs b/backend/API/Controllers/RecipeController.cs index d2c65ea..76a49d5 100644 --- a/backend/API/Controllers/RecipeController.cs +++ b/backend/API/Controllers/RecipeController.cs @@ -52,7 +52,7 @@ namespace API.Controllers /// The recipe to be added /// returns a okobjectresult with a boolean that is true if it fails it returns a confliftobjectresult with a message of why it failed [Authorize] - [HttpPost("create/{RecipesId}")] + [HttpPost("create")] public async Task CreateRecipe([FromBody] RecipeDTO recipe) { var claims = HttpContext.User.Claims;