Move date and meal fields to each line (WIP)

Frontend only, no backend support yet.
This commit is contained in:
Christopher C. Wells 2021-01-24 13:07:42 -08:00
parent 7526138ad2
commit 309ec6810c
8 changed files with 109 additions and 71 deletions

View File

@ -9,6 +9,7 @@ use App\Models\Food;
use App\Models\JournalEntry;
use App\Models\Recipe;
use App\Rules\ArrayNotEmpty;
use App\Rules\InArray;
use App\Rules\StringIsDecimalOrFraction;
use App\Rules\UsesIngredientTrait;
use App\Support\Number;
@ -44,10 +45,18 @@ class JournalEntryController extends Controller
$ingredients = [];
if ($old = old('ingredients')) {
foreach ($old['amount'] as $key => $amount) {
if (empty($amount) && empty($old['unit'][$key]) && empty($old['id'][$key])) {
if (
empty($old['date'][$key])
&& empty($old['meal'][$key])
&& empty($amount)
&& empty($old['unit'][$key])
&& empty($old['id'][$key])
) {
continue;
}
$ingredients[] = [
'date' => $old['date'][$key],
'meal' => $old['meal'][$key],
'amount' => $amount,
'unit' => $old['unit'][$key],
'id' => $old['id'][$key],
@ -59,12 +68,7 @@ class JournalEntryController extends Controller
return view('journal-entries.create')
->with('ingredients', $ingredients)
->with('meals', [
['value' => 'breakfast', 'label' => 'Breakfast'],
['value' => 'lunch', 'label' => 'Lunch'],
['value' => 'dinner', 'label' => 'Dinner'],
['value' => 'snacks', 'label' => 'Snacks'],
])
->with('meals', JournalEntry::$meals)
->with('units', [
['value' => 'tsp', 'label' => 'tsp.'],
['value' => 'tbsp', 'label' => 'tbsp.'],
@ -81,12 +85,14 @@ class JournalEntryController extends Controller
public function store(Request $request): RedirectResponse
{
$input = $request->validate([
'date' => 'required|date',
'meal' => 'required|string',
'ingredients.date' => ['required', 'array', new ArrayNotEmpty],
'ingredients.date.*' => ['nullable', 'date', 'required_with:ingredients.id.*'],
'ingredients.meal' => ['required', 'array', new ArrayNotEmpty],
'ingredients.meal.*' => ['nullable', 'string', 'required_with:ingredients.id.*', new InArray(array_column(JournalEntry::$meals, 'value'))],
'ingredients.amount' => ['required', 'array', new ArrayNotEmpty],
'ingredients.amount.*' => ['required_with:foods.*,recipes.*', 'nullable', new StringIsDecimalOrFraction],
'ingredients.unit' => 'required|array',
'ingredients.unit.*' => 'nullable|string',
'ingredients.amount.*' => ['required_with:ingredients.id.*', 'nullable', new StringIsDecimalOrFraction],
'ingredients.unit' => ['required', 'array'],
'ingredients.unit.*' => ['nullable', 'string'],
'ingredients.id' => ['required', 'array', new ArrayNotEmpty],
'ingredients.id.*' => 'required_with:ingredients.amount.*|nullable',
'ingredients.type' => ['required', 'array', new ArrayNotEmpty],

View File

@ -86,6 +86,16 @@ class JournalEntry extends Model
*/
protected $with = ['user', 'foods:id,name,slug', 'recipes:id,name,slug'];
/**
* Valid meal options.
*/
public static array $meals = [
['value' => 'breakfast', 'label' => 'Breakfast'],
['value' => 'lunch', 'label' => 'Lunch'],
['value' => 'dinner', 'label' => 'Dinner'],
['value' => 'snacks', 'label' => 'Snacks'],
];
/**
* Get the User this entry belongs to.
*/

View File

@ -7,11 +7,7 @@ use Illuminate\Contracts\Validation\Rule;
class ArrayNotEmpty implements Rule
{
/**
* Determine if the array is empty.
*
* @param string $attribute
* @param mixed $value
* @return bool
* {@inheritdoc}
*/
public function passes($attribute, $value): bool
{
@ -19,9 +15,7 @@ class ArrayNotEmpty implements Rule
}
/**
* Get the validation error message.
*
* @return string
* {@inheritdoc}
*/
public function message(): string
{

40
app/Rules/InArray.php Normal file
View File

@ -0,0 +1,40 @@
<?php
namespace App\Rules;
use Illuminate\Contracts\Validation\Rule;
class InArray implements Rule
{
/**
* Array to validate against.
*/
private array $array;
/**
* InArray constructor.
*
* @param array $array
* Array to use for validation.
*/
public function __construct(array $array) {
$this->array = $array;
}
/**
* {@inheritdoc}
*/
public function passes($attribute, $value): bool
{
return in_array($value, $this->array);
}
/**
* {@inheritdoc}
*/
public function message(): string
{
return 'Invalid :attribute value :input.';
}
}

View File

@ -8,11 +8,7 @@ use Illuminate\Contracts\Validation\Rule;
class StringIsDecimalOrFraction implements Rule
{
/**
* Determine if the string is a decimal or fraction, excluding zero.
*
* @param string $attribute
* @param mixed $value
* @return bool
* {@inheritdoc}
*/
public function passes($attribute, $value): bool
{
@ -27,9 +23,7 @@ class StringIsDecimalOrFraction implements Rule
}
/**
* Get the validation error message.
*
* @return string
* {@inheritdoc}
*/
public function message(): string
{

View File

@ -8,11 +8,7 @@ use Illuminate\Contracts\Validation\Rule;
class UsesIngredientTrait implements Rule
{
/**
* Determine if the array is empty.
*
* @param string $attribute
* @param mixed $value
* @return bool
* {@inheritdoc}
*/
public function passes($attribute, $value): bool
{
@ -23,9 +19,7 @@ class UsesIngredientTrait implements Rule
}
/**
* Get the validation error message.
*
* @return string
* {@inheritdoc}
*/
public function message(): string
{

View File

@ -11,39 +11,13 @@
<div class="p-6 bg-white border-b border-gray-200">
<form method="POST" action="{{ route('journal-entries.store') }}">
@csrf
<div class="flex flex-row mb-4 space-x-4">
<!-- Date -->
<div>
<x-inputs.label for="date" :value="__('Date')"/>
<x-inputs.input id="date"
class="block mt-1"
type="date"
name="date"
:value="old('date', \Illuminate\Support\Carbon::now()->toDateString())"
required />
</div>
<!-- Meal -->
<div>
<x-inputs.label for="meal" :value="__('Meal')"/>
<x-inputs.select name="meal"
class="block mt-1"
:options="$meals"
:selectedValue="old('meal')"
required>
<option value=""></option>
</x-inputs.select>
</div>
</div>
<!-- Items -->
<div x-data="{ ingredients: 0 }">
<div x-data="{ ingredients: {{ empty($ingredients) ? 0 : -1 }} }">
<div class="grid grid-cols-12 gap-4 items-center">
<x-inputs.label for="amounts" value="Amount"/>
<x-inputs.label for="units" value="Unit" class="col-span-2"/>
<x-inputs.label for="foods" value="Food or Recipe" class="col-span-8"/>
<x-inputs.label for="ingredients[date][]" value="Date" class="col-span-2"/>
<x-inputs.label for="ingredients[meal][]" value="Meal" class="col-span-2"/>
<x-inputs.label for="ingredients[amount][]" value="Amount"/>
<x-inputs.label for="ingredients[unit][]" value="Unit" class="col-span-2"/>
<x-inputs.label for="ingredients[id][]" value="Food or Recipe" class="col-span-4"/>
</div>
<div>
@foreach($ingredients as $ingredient)

View File

@ -1,10 +1,33 @@
<div class="grid grid-cols-12 gap-4 items-center mt-2">
<!-- Date -->
<div class="col-span-2">
<x-inputs.input class="block w-full"
type="date"
name="ingredients[date][]"
:value="$date ?? \Illuminate\Support\Carbon::now()->toDateString()"
required />
</div>
<!-- Meal -->
<div class="col-span-2">
<x-inputs.select name="ingredients[meal][]"
class="block w-full"
:options="$meals"
:selectedValue="$meal ?? null"
required>
<option value=""></option>
</x-inputs.select>
</div>
<!-- Amount -->
<div>
<x-inputs.input type="text"
name="ingredients[amount][]"
class="block w-full"
:value="$amount ?? null" />
</div>
<!-- Unit -->
<div class="col-span-2">
<x-inputs.select name="ingredients[unit][]"
class="block w-full"
@ -13,13 +36,16 @@
<option value=""></option>
</x-inputs.select>
</div>
<div class="col-span-8">
<!-- Ingredient -->
<div class="col-span-4">
<x-ingredient-picker :default-id="$id ?? null"
:default-type="$type ?? null"
:default-name="$name ?? null" />
</div>
<x-inputs.icon-button type="button" color="red" x-on:click="$event.target.parentNode.remove();">
<svg class="h-8 w-8 pointer-events-none" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 20 20" fill="currentColor">
<x-inputs.icon-button type="button" color="red" x-on:click="$event.target.parentNode.remove(); ingredients--;">
<svg class="h-8 w-8 pointer-events-none m-auto" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 20 20" fill="currentColor">
<path fill-rule="evenodd" d="M9 2a1 1 0 00-.894.553L7.382 4H4a1 1 0 000 2v10a2 2 0 002 2h8a2 2 0 002-2V6a1 1 0 100-2h-3.382l-.724-1.447A1 1 0 0011 2H9zM7 8a1 1 0 012 0v6a1 1 0 11-2 0V8zm5-1a1 1 0 00-1 1v6a1 1 0 102 0V8a1 1 0 00-1-1z" clip-rule="evenodd" />
</svg>
</x-inputs.icon-button>