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') {