diff --git a/app/Http/Controllers/JournalEntryController.php b/app/Http/Controllers/JournalEntryController.php
index 43df249..44b70d2 100644
--- a/app/Http/Controllers/JournalEntryController.php
+++ b/app/Http/Controllers/JournalEntryController.php
@@ -82,7 +82,7 @@ class JournalEntryController extends Controller
) {
continue;
}
- $ingredients[] = [
+ $ingredients[$key] = [
'date' => $old['date'][$key],
'meal' => $old['meal'][$key],
'amount' => $amount,
@@ -91,6 +91,18 @@ class JournalEntryController extends Controller
'type' => $old['type'][$key],
'name' => $old['name'][$key],
];
+
+ // Add supported units for the ingredient.
+ $ingredient = NULL;
+ if ($ingredients[$key]['type'] === Food::class) {
+ $ingredient = Food::whereId($ingredients[$key]['id'])->first();
+ }
+ elseif ($ingredients[$key]['type'] === Recipe::class) {
+ $ingredient = Recipe::whereId($ingredients[$key]['id'])->first();
+ }
+ if ($ingredient) {
+ $ingredients[$key]['units'] = $ingredient->units_supported;
+ }
}
}
diff --git a/app/JsonApi/Schemas/FoodSchema.php b/app/JsonApi/Schemas/FoodSchema.php
index 8fac8d9..9336d58 100644
--- a/app/JsonApi/Schemas/FoodSchema.php
+++ b/app/JsonApi/Schemas/FoodSchema.php
@@ -43,6 +43,7 @@ class FoodSchema extends SchemaProvider
'servingUnit' => $resource->serving_unit,
'servingUnitFormatted' => $resource->serving_unit_formatted,
'servingWeight' => $resource->serving_weight,
+ 'unitsSupported' => $resource->units_supported->pluck('value'),
'createdAt' => $resource->created_at,
'updatedAt' => $resource->updated_at,
'showUrl' => route('foods.show', $resource),
diff --git a/app/JsonApi/Schemas/RecipeSchema.php b/app/JsonApi/Schemas/RecipeSchema.php
index 9dd75b6..b45b80e 100644
--- a/app/JsonApi/Schemas/RecipeSchema.php
+++ b/app/JsonApi/Schemas/RecipeSchema.php
@@ -36,6 +36,7 @@ class RecipeSchema extends SchemaProvider
'servings' => $resource->servings,
'weight' => $resource->weight,
'serving_weight' => $resource->serving_weight,
+ 'units_supported' => $resource->units_supported->pluck('label'),
'caloriesPerServing' => $resource->caloriesPerServing(),
'carbohydratesPerServing' => $resource->carbohydratesPerServing(),
'cholesterolPerServing' => $resource->cholesterolPerServing(),
diff --git a/app/Models/Food.php b/app/Models/Food.php
index 618b2e7..8326d18 100644
--- a/app/Models/Food.php
+++ b/app/Models/Food.php
@@ -73,6 +73,7 @@ use Laravel\Scout\Searchable;
* @mixin \Eloquent
* @method static \Illuminate\Database\Eloquent\Builder|Food withUniqueSlugConstraints(\Illuminate\Database\Eloquent\Model $model, string $attribute, array $config, string $slug)
* @method static \Database\Factories\FoodFactory factory(...$parameters)
+ * @property-read \Illuminate\Support\Collection $units_supported
*/
final class Food extends Model
{
@@ -129,7 +130,8 @@ final class Food extends Model
*/
protected $appends = [
'serving_size_formatted',
- 'serving_unit_formatted'
+ 'serving_unit_formatted',
+ 'units_supported',
];
/**
diff --git a/app/Models/Recipe.php b/app/Models/Recipe.php
index 6184263..6821c20 100644
--- a/app/Models/Recipe.php
+++ b/app/Models/Recipe.php
@@ -77,6 +77,7 @@ use Spatie\MediaLibrary\MediaCollections\Models\Media;
* @property-read Collection $ingredients_list
* @property-read \Illuminate\Database\Eloquent\Collection|\App\Models\RecipeSeparator[] $separators
* @property-read int|null $separators_count
+ * @property-read Collection $units_supported
*/
final class Recipe extends Model implements HasMedia
{
@@ -132,6 +133,7 @@ final class Recipe extends Model implements HasMedia
protected $appends = [
'serving_weight',
'time_total',
+ 'units_supported'
];
/**
diff --git a/app/Models/Traits/Ingredient.php b/app/Models/Traits/Ingredient.php
index c3269b7..a81c339 100644
--- a/app/Models/Traits/Ingredient.php
+++ b/app/Models/Traits/Ingredient.php
@@ -3,8 +3,10 @@
namespace App\Models\Traits;
use App\Models\IngredientAmount;
-use Illuminate\Database\Eloquent\Collection;
+use App\Support\Nutrients;
+use Illuminate\Database\Eloquent\Collection as DatabaseCollection;
use Illuminate\Database\Eloquent\Relations\MorphMany;
+use Illuminate\Support\Collection;
use Illuminate\Support\Facades\DB;
use Spatie\Tags\Tag;
@@ -40,7 +42,7 @@ trait Ingredient
*
* @see \Spatie\Tags\HasTags
*/
- public static function getTagTotals(string $locale = null): Collection {
+ public static function getTagTotals(string $locale = null): DatabaseCollection {
$locale = $locale ?? app()->getLocale();
return Tag::query()->join('taggables', 'taggables.tag_id', '=', 'id')
->select(['id', 'name', DB::raw('count(*) as total')])
@@ -49,4 +51,20 @@ trait Ingredient
->orderBy("name->{$locale}")
->get();
}
+
+ /**
+ * Get a collection of units supported by this ingredient.
+ */
+ public function getUnitsSupportedAttribute(): Collection {
+ $units = Nutrients::units();
+ $supported = $units->where('value', 'serving');
+ if (!empty($this->serving_unit)) {
+ $type = $units->where('value', $this->serving_unit)->pluck('type')->first();
+ $supported = $supported->merge($units->where('type', $type));
+ }
+ if (!empty($this->serving_weight)) {
+ $supported = $supported->merge($units->where('type', 'weight'));
+ }
+ return $supported->sortBy('label');
+ }
}
diff --git a/app/Models/User.php b/app/Models/User.php
index 11a3248..ee23a60 100644
--- a/app/Models/User.php
+++ b/app/Models/User.php
@@ -38,6 +38,8 @@ use Illuminate\Support\Facades\Auth;
* @property-read \Illuminate\Database\Eloquent\Collection|\App\Models\Goal[] $goals
* @property-read int|null $goals_count
* @method static \Database\Factories\UserFactory factory(...$parameters)
+ * @property-read \Illuminate\Database\Eloquent\Collection|\App\Models\JournalEntry[] $journalEntries
+ * @property-read int|null $journal_entries_count
*/
final class User extends Authenticatable
{
diff --git a/app/Support/Nutrients.php b/app/Support/Nutrients.php
index 6cc61c0..6237e71 100644
--- a/app/Support/Nutrients.php
+++ b/app/Support/Nutrients.php
@@ -16,6 +16,7 @@ class Nutrients
* Each entry has two keys:
* - value: Machine name for the unit.
* - label: Human-readable name for the unit.
+ * - type: Unit type -- matching types can be converted.
*
* @return \Illuminate\Support\Collection
*/
@@ -23,27 +24,33 @@ class Nutrients
return new Collection([
'cup' => [
'value' => 'cup',
- 'label' => 'cup'
+ 'label' => 'cup',
+ 'type' => 'volume',
],
'gram' => [
'value' => 'gram',
- 'label' => 'gram'
+ 'label' => 'gram',
+ 'type' => 'weight',
],
'oz' => [
'value' => 'oz',
- 'label' => 'oz'
+ 'label' => 'oz',
+ 'type' => 'weight',
],
'serving' => [
'value' => 'serving',
- 'label' => 'serving'
+ 'label' => 'serving',
+ 'type' => 'division',
],
'tbsp' => [
'value' => 'tbsp',
- 'label' => 'tbsp.'
+ 'label' => 'tbsp.',
+ 'type' => 'volume',
],
'tsp' => [
'value' => 'tsp',
- 'label' => 'tsp.'
+ 'label' => 'tsp.',
+ 'type' => 'volume',
],
]);
}
diff --git a/resources/views/journal-entries/partials/entry-item-input.blade.php b/resources/views/journal-entries/partials/entry-item-input.blade.php
index 1f79a26..bc120ee 100644
--- a/resources/views/journal-entries/partials/entry-item-input.blade.php
+++ b/resources/views/journal-entries/partials/entry-item-input.blade.php
@@ -46,7 +46,7 @@
@@ -74,7 +74,20 @@
window.addEventListener('ingredient-picked', (e) => {
const entryItem = e.target.closest('.entry-item');
const ingredient = e.detail.ingredient;
- let servingSize, servingUnit;
+ let servingSize, servingUnit
+
+ // Restrict unit select list values to supported units.
+ const unitsSelectList = entryItem.querySelector(':scope select[name="ingredients[unit][]"]');
+ for (const [key, option] in unitsSelectList.options) {
+ unitsSelectList.remove(key);
+ }
+ for (const key in ingredient.units_supported) {
+ const unit = ingredient.units_supported[key];
+ const option = document.createElement('option');
+ option.value = unit.value;
+ option.text = unit.label;
+ unitsSelectList.add(option);
+ }
// Always set recipes to a default of 1 serving.
if (ingredient.type === 'App\\Models\\Recipe') {