mirror of https://github.com/kcal-app/kcal.git
Move date and meal fields to each line (WIP)
Frontend only, no backend support yet.
This commit is contained in:
parent
7526138ad2
commit
309ec6810c
|
|
@ -9,6 +9,7 @@ use App\Models\Food;
|
||||||
use App\Models\JournalEntry;
|
use App\Models\JournalEntry;
|
||||||
use App\Models\Recipe;
|
use App\Models\Recipe;
|
||||||
use App\Rules\ArrayNotEmpty;
|
use App\Rules\ArrayNotEmpty;
|
||||||
|
use App\Rules\InArray;
|
||||||
use App\Rules\StringIsDecimalOrFraction;
|
use App\Rules\StringIsDecimalOrFraction;
|
||||||
use App\Rules\UsesIngredientTrait;
|
use App\Rules\UsesIngredientTrait;
|
||||||
use App\Support\Number;
|
use App\Support\Number;
|
||||||
|
|
@ -44,10 +45,18 @@ class JournalEntryController extends Controller
|
||||||
$ingredients = [];
|
$ingredients = [];
|
||||||
if ($old = old('ingredients')) {
|
if ($old = old('ingredients')) {
|
||||||
foreach ($old['amount'] as $key => $amount) {
|
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;
|
continue;
|
||||||
}
|
}
|
||||||
$ingredients[] = [
|
$ingredients[] = [
|
||||||
|
'date' => $old['date'][$key],
|
||||||
|
'meal' => $old['meal'][$key],
|
||||||
'amount' => $amount,
|
'amount' => $amount,
|
||||||
'unit' => $old['unit'][$key],
|
'unit' => $old['unit'][$key],
|
||||||
'id' => $old['id'][$key],
|
'id' => $old['id'][$key],
|
||||||
|
|
@ -59,12 +68,7 @@ class JournalEntryController extends Controller
|
||||||
|
|
||||||
return view('journal-entries.create')
|
return view('journal-entries.create')
|
||||||
->with('ingredients', $ingredients)
|
->with('ingredients', $ingredients)
|
||||||
->with('meals', [
|
->with('meals', JournalEntry::$meals)
|
||||||
['value' => 'breakfast', 'label' => 'Breakfast'],
|
|
||||||
['value' => 'lunch', 'label' => 'Lunch'],
|
|
||||||
['value' => 'dinner', 'label' => 'Dinner'],
|
|
||||||
['value' => 'snacks', 'label' => 'Snacks'],
|
|
||||||
])
|
|
||||||
->with('units', [
|
->with('units', [
|
||||||
['value' => 'tsp', 'label' => 'tsp.'],
|
['value' => 'tsp', 'label' => 'tsp.'],
|
||||||
['value' => 'tbsp', 'label' => 'tbsp.'],
|
['value' => 'tbsp', 'label' => 'tbsp.'],
|
||||||
|
|
@ -81,12 +85,14 @@ class JournalEntryController extends Controller
|
||||||
public function store(Request $request): RedirectResponse
|
public function store(Request $request): RedirectResponse
|
||||||
{
|
{
|
||||||
$input = $request->validate([
|
$input = $request->validate([
|
||||||
'date' => 'required|date',
|
'ingredients.date' => ['required', 'array', new ArrayNotEmpty],
|
||||||
'meal' => 'required|string',
|
'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', 'array', new ArrayNotEmpty],
|
||||||
'ingredients.amount.*' => ['required_with:foods.*,recipes.*', 'nullable', new StringIsDecimalOrFraction],
|
'ingredients.amount.*' => ['required_with:ingredients.id.*', 'nullable', new StringIsDecimalOrFraction],
|
||||||
'ingredients.unit' => 'required|array',
|
'ingredients.unit' => ['required', 'array'],
|
||||||
'ingredients.unit.*' => 'nullable|string',
|
'ingredients.unit.*' => ['nullable', 'string'],
|
||||||
'ingredients.id' => ['required', 'array', new ArrayNotEmpty],
|
'ingredients.id' => ['required', 'array', new ArrayNotEmpty],
|
||||||
'ingredients.id.*' => 'required_with:ingredients.amount.*|nullable',
|
'ingredients.id.*' => 'required_with:ingredients.amount.*|nullable',
|
||||||
'ingredients.type' => ['required', 'array', new ArrayNotEmpty],
|
'ingredients.type' => ['required', 'array', new ArrayNotEmpty],
|
||||||
|
|
|
||||||
|
|
@ -86,6 +86,16 @@ class JournalEntry extends Model
|
||||||
*/
|
*/
|
||||||
protected $with = ['user', 'foods:id,name,slug', 'recipes:id,name,slug'];
|
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.
|
* Get the User this entry belongs to.
|
||||||
*/
|
*/
|
||||||
|
|
|
||||||
|
|
@ -7,11 +7,7 @@ use Illuminate\Contracts\Validation\Rule;
|
||||||
class ArrayNotEmpty implements Rule
|
class ArrayNotEmpty implements Rule
|
||||||
{
|
{
|
||||||
/**
|
/**
|
||||||
* Determine if the array is empty.
|
* {@inheritdoc}
|
||||||
*
|
|
||||||
* @param string $attribute
|
|
||||||
* @param mixed $value
|
|
||||||
* @return bool
|
|
||||||
*/
|
*/
|
||||||
public function passes($attribute, $value): bool
|
public function passes($attribute, $value): bool
|
||||||
{
|
{
|
||||||
|
|
@ -19,9 +15,7 @@ class ArrayNotEmpty implements Rule
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Get the validation error message.
|
* {@inheritdoc}
|
||||||
*
|
|
||||||
* @return string
|
|
||||||
*/
|
*/
|
||||||
public function message(): string
|
public function message(): string
|
||||||
{
|
{
|
||||||
|
|
|
||||||
|
|
@ -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.';
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -8,11 +8,7 @@ use Illuminate\Contracts\Validation\Rule;
|
||||||
class StringIsDecimalOrFraction implements Rule
|
class StringIsDecimalOrFraction implements Rule
|
||||||
{
|
{
|
||||||
/**
|
/**
|
||||||
* Determine if the string is a decimal or fraction, excluding zero.
|
* {@inheritdoc}
|
||||||
*
|
|
||||||
* @param string $attribute
|
|
||||||
* @param mixed $value
|
|
||||||
* @return bool
|
|
||||||
*/
|
*/
|
||||||
public function passes($attribute, $value): bool
|
public function passes($attribute, $value): bool
|
||||||
{
|
{
|
||||||
|
|
@ -27,9 +23,7 @@ class StringIsDecimalOrFraction implements Rule
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Get the validation error message.
|
* {@inheritdoc}
|
||||||
*
|
|
||||||
* @return string
|
|
||||||
*/
|
*/
|
||||||
public function message(): string
|
public function message(): string
|
||||||
{
|
{
|
||||||
|
|
|
||||||
|
|
@ -8,11 +8,7 @@ use Illuminate\Contracts\Validation\Rule;
|
||||||
class UsesIngredientTrait implements Rule
|
class UsesIngredientTrait implements Rule
|
||||||
{
|
{
|
||||||
/**
|
/**
|
||||||
* Determine if the array is empty.
|
* {@inheritdoc}
|
||||||
*
|
|
||||||
* @param string $attribute
|
|
||||||
* @param mixed $value
|
|
||||||
* @return bool
|
|
||||||
*/
|
*/
|
||||||
public function passes($attribute, $value): bool
|
public function passes($attribute, $value): bool
|
||||||
{
|
{
|
||||||
|
|
@ -23,9 +19,7 @@ class UsesIngredientTrait implements Rule
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Get the validation error message.
|
* {@inheritdoc}
|
||||||
*
|
|
||||||
* @return string
|
|
||||||
*/
|
*/
|
||||||
public function message(): string
|
public function message(): string
|
||||||
{
|
{
|
||||||
|
|
|
||||||
|
|
@ -11,39 +11,13 @@
|
||||||
<div class="p-6 bg-white border-b border-gray-200">
|
<div class="p-6 bg-white border-b border-gray-200">
|
||||||
<form method="POST" action="{{ route('journal-entries.store') }}">
|
<form method="POST" action="{{ route('journal-entries.store') }}">
|
||||||
@csrf
|
@csrf
|
||||||
<div class="flex flex-row mb-4 space-x-4">
|
<div x-data="{ ingredients: {{ empty($ingredients) ? 0 : -1 }} }">
|
||||||
<!-- 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 class="grid grid-cols-12 gap-4 items-center">
|
<div class="grid grid-cols-12 gap-4 items-center">
|
||||||
<x-inputs.label for="amounts" value="Amount"/>
|
<x-inputs.label for="ingredients[date][]" value="Date" class="col-span-2"/>
|
||||||
<x-inputs.label for="units" value="Unit" class="col-span-2"/>
|
<x-inputs.label for="ingredients[meal][]" value="Meal" class="col-span-2"/>
|
||||||
<x-inputs.label for="foods" value="Food or Recipe" class="col-span-8"/>
|
<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>
|
||||||
<div>
|
<div>
|
||||||
@foreach($ingredients as $ingredient)
|
@foreach($ingredients as $ingredient)
|
||||||
|
|
|
||||||
|
|
@ -1,10 +1,33 @@
|
||||||
<div class="grid grid-cols-12 gap-4 items-center mt-2">
|
<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>
|
<div>
|
||||||
<x-inputs.input type="text"
|
<x-inputs.input type="text"
|
||||||
name="ingredients[amount][]"
|
name="ingredients[amount][]"
|
||||||
class="block w-full"
|
class="block w-full"
|
||||||
:value="$amount ?? null" />
|
:value="$amount ?? null" />
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
<!-- Unit -->
|
||||||
<div class="col-span-2">
|
<div class="col-span-2">
|
||||||
<x-inputs.select name="ingredients[unit][]"
|
<x-inputs.select name="ingredients[unit][]"
|
||||||
class="block w-full"
|
class="block w-full"
|
||||||
|
|
@ -13,13 +36,16 @@
|
||||||
<option value=""></option>
|
<option value=""></option>
|
||||||
</x-inputs.select>
|
</x-inputs.select>
|
||||||
</div>
|
</div>
|
||||||
<div class="col-span-8">
|
|
||||||
|
<!-- Ingredient -->
|
||||||
|
<div class="col-span-4">
|
||||||
<x-ingredient-picker :default-id="$id ?? null"
|
<x-ingredient-picker :default-id="$id ?? null"
|
||||||
:default-type="$type ?? null"
|
:default-type="$type ?? null"
|
||||||
:default-name="$name ?? null" />
|
:default-name="$name ?? null" />
|
||||||
</div>
|
</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" />
|
<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>
|
</svg>
|
||||||
</x-inputs.icon-button>
|
</x-inputs.icon-button>
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue