Refactor goals with a "frequency" attribute

This commit is contained in:
Christopher C. Wells 2021-02-15 09:08:28 -08:00
parent a26a4c71d0
commit 3a36f648db
9 changed files with 83 additions and 52 deletions

View File

@ -24,8 +24,7 @@ class GoalController extends Controller
} }
return view('goals.index') return view('goals.index')
->with('date', $date) ->with('date', $date)
->with('goals', Auth::user()->getGoalsByTime($date)) ->with('goals', Auth::user()->getGoalsByTime($date));
->with('goalOptions', Goal::getGoalOptions());
} }
/** /**
@ -61,7 +60,8 @@ class GoalController extends Controller
{ {
return view('goals.edit') return view('goals.edit')
->with('goal', $goal) ->with('goal', $goal)
->with('goalOptions', Goal::getGoalOptions()); ->with('nameOptions', Goal::getNameOptions())
->with('frequencyOptions', Goal::$frequencyOptions);
} }
/** /**
@ -72,11 +72,11 @@ class GoalController extends Controller
$attributes = $request->validate([ $attributes = $request->validate([
'from' => ['nullable', 'date'], 'from' => ['nullable', 'date'],
'to' => ['nullable', 'date'], 'to' => ['nullable', 'date'],
'goal' => ['required', 'string'], 'frequency' => ['nullable', 'string'],
'amount' => ['required', 'numeric'], 'name' => ['required', 'string'],
'goal' => ['required', 'numeric'],
]); ]);
$goal->fill(array_filter($attributes)) $goal->fill($attributes)->user()->associate(Auth::user());
->user()->associate(Auth::user());
$goal->save(); $goal->save();
session()->flash('message', "Goal updated!"); session()->flash('message', "Goal updated!");
return redirect()->route('goals.show', $goal); return redirect()->route('goals.show', $goal);
@ -87,9 +87,7 @@ class GoalController extends Controller
*/ */
public function delete(Goal $goal): View public function delete(Goal $goal): View
{ {
return view('goals.delete') return view('goals.delete')->with('goal', $goal);
->with('goal', $goal)
->with('goalOptions', Goal::getGoalOptions());
} }
/** /**

View File

@ -14,19 +14,22 @@ use Illuminate\Database\Eloquent\Relations\BelongsTo;
* @property int $user_id * @property int $user_id
* @property \datetime|null $from * @property \datetime|null $from
* @property \datetime|null $to * @property \datetime|null $to
* @property string $goal * @property string|null $frequency
* @property float $amount * @property string $name
* @property float $goal
* @property \Illuminate\Support\Carbon|null $created_at * @property \Illuminate\Support\Carbon|null $created_at
* @property \Illuminate\Support\Carbon|null $updated_at * @property \Illuminate\Support\Carbon|null $updated_at
* @property-read string $summary
* @property-read \App\Models\User $user * @property-read \App\Models\User $user
* @method static \Illuminate\Database\Eloquent\Builder|Goal newModelQuery() * @method static \Illuminate\Database\Eloquent\Builder|Goal newModelQuery()
* @method static \Illuminate\Database\Eloquent\Builder|Goal newQuery() * @method static \Illuminate\Database\Eloquent\Builder|Goal newQuery()
* @method static \Illuminate\Database\Eloquent\Builder|Goal query() * @method static \Illuminate\Database\Eloquent\Builder|Goal query()
* @method static \Illuminate\Database\Eloquent\Builder|Goal whereAmount($value)
* @method static \Illuminate\Database\Eloquent\Builder|Goal whereCreatedAt($value) * @method static \Illuminate\Database\Eloquent\Builder|Goal whereCreatedAt($value)
* @method static \Illuminate\Database\Eloquent\Builder|Goal whereFrequency($value)
* @method static \Illuminate\Database\Eloquent\Builder|Goal whereFrom($value) * @method static \Illuminate\Database\Eloquent\Builder|Goal whereFrom($value)
* @method static \Illuminate\Database\Eloquent\Builder|Goal whereGoal($value) * @method static \Illuminate\Database\Eloquent\Builder|Goal whereGoal($value)
* @method static \Illuminate\Database\Eloquent\Builder|Goal whereId($value) * @method static \Illuminate\Database\Eloquent\Builder|Goal whereId($value)
* @method static \Illuminate\Database\Eloquent\Builder|Goal whereName($value)
* @method static \Illuminate\Database\Eloquent\Builder|Goal whereTo($value) * @method static \Illuminate\Database\Eloquent\Builder|Goal whereTo($value)
* @method static \Illuminate\Database\Eloquent\Builder|Goal whereUpdatedAt($value) * @method static \Illuminate\Database\Eloquent\Builder|Goal whereUpdatedAt($value)
* @method static \Illuminate\Database\Eloquent\Builder|Goal whereUserId($value) * @method static \Illuminate\Database\Eloquent\Builder|Goal whereUserId($value)
@ -36,14 +39,22 @@ final class Goal extends Model
{ {
use HasFactory; use HasFactory;
/**
* Supported options for thr frequency attribute.
*/
public static array $frequencyOptions = [
['value' => 'daily', 'label' => 'daily'],
];
/** /**
* @inheritdoc * @inheritdoc
*/ */
protected $fillable = [ protected $fillable = [
'frequency',
'from', 'from',
'to',
'goal', 'goal',
'amount', 'name',
'to',
]; ];
/** /**
@ -51,8 +62,15 @@ final class Goal extends Model
*/ */
protected $casts = [ protected $casts = [
'from' => 'datetime:Y-m-d', 'from' => 'datetime:Y-m-d',
'goal' => 'float',
'to' => 'datetime:Y-m-d', 'to' => 'datetime:Y-m-d',
'amount' => 'float', ];
/**
* @inheritdoc
*/
protected $appends = [
'summary',
]; ];
/** /**
@ -62,18 +80,20 @@ final class Goal extends Model
return $this->belongsTo(User::class); return $this->belongsTo(User::class);
} }
public function getSummaryAttribute(): string {
$nameOptions = self::getNameOptions();
return number_format($this->goal) . "{$nameOptions[$this->name]['unit']} {$nameOptions[$this->name]['label']} {$this->frequency}";
}
/** /**
* Get options for the "goal" column. * Get options for the "name" column.
*
* @return array
*/ */
public static function getGoalOptions(): array { public static function getNameOptions(): array {
$options = []; $options = [];
foreach (Nutrients::$all as $nutrient) { foreach (Nutrients::$all as $nutrient) {
$key = "{$nutrient['value']}_per_day"; $options[$nutrient['value']] = [
$options[$key] = [ 'value' => $nutrient['value'],
'value' => $key, 'label' => $nutrient['label'],
'label' => "{$nutrient['value']} per day",
'unit' => $nutrient['unit'], 'unit' => $nutrient['unit'],
]; ];
} }

View File

@ -7,6 +7,7 @@ use Illuminate\Database\Eloquent\Relations\HasMany;
use Illuminate\Foundation\Auth\User as Authenticatable; use Illuminate\Foundation\Auth\User as Authenticatable;
use Illuminate\Notifications\Notifiable; use Illuminate\Notifications\Notifiable;
use Illuminate\Support\Carbon; use Illuminate\Support\Carbon;
use Illuminate\Support\Collection;
use Illuminate\Support\Facades\Auth; use Illuminate\Support\Facades\Auth;
/** /**
@ -79,7 +80,7 @@ final class User extends Authenticatable
*/ */
public function getGoalsByTime(?Carbon $date = null): array { public function getGoalsByTime(?Carbon $date = null): array {
$now = $date ?? Carbon::now(); $now = $date ?? Carbon::now();
$goals = ['past' => [], 'present' => [], 'future' => []]; $goals = ['past' => new Collection(), 'present' => new Collection(), 'future' => new Collection()];
Goal::all()->where('user_id', Auth::user()->id) Goal::all()->where('user_id', Auth::user()->id)
->each(function ($item) use(&$goals, $now) { ->each(function ($item) use(&$goals, $now) {
if ($item->to && $now->isAfter($item->to)) { if ($item->to && $now->isAfter($item->to)) {

View File

@ -10,21 +10,21 @@ class Nutrients
public static float $gramsPerOunce = 28.349523125; public static float $gramsPerOunce = 28.349523125;
public static array $all = [ public static array $all = [
['value' => 'calories', 'unit' => null], ['value' => 'calories', 'label' => 'calories', 'unit' => null],
['value' => 'fat', 'unit' => 'g'], ['value' => 'carbohydrates', 'label' => 'carbohydrates', 'unit' => 'g'],
['value' => 'cholesterol', 'unit' => 'mg'], ['value' => 'cholesterol', 'label' => 'cholesterol', 'unit' => 'mg'],
['value' => 'sodium', 'unit' => 'mg'], ['value' => 'fat', 'label' => 'fat', 'unit' => 'g'],
['value' => 'carbohydrates', 'unit' => 'g'], ['value' => 'protein', 'label' => 'protein', 'unit' => 'g'],
['value' => 'protein', 'unit' => 'g'], ['value' => 'sodium', 'label' => 'sodium', 'unit' => 'mg'],
]; ];
public static array $units = [ public static array $units = [
['value' => 'tsp', 'label' => 'tsp.'],
['value' => 'tbsp', 'label' => 'tbsp.'],
['value' => 'cup', 'label' => 'cup'], ['value' => 'cup', 'label' => 'cup'],
['value' => 'oz', 'label' => 'oz'],
['value' => 'gram', 'label' => 'grams'], ['value' => 'gram', 'label' => 'grams'],
['value' => 'oz', 'label' => 'oz'],
['value' => 'serving', 'label' => 'servings'], ['value' => 'serving', 'label' => 'servings'],
['value' => 'tbsp', 'label' => 'tbsp.'],
['value' => 'tsp', 'label' => 'tsp.'],
]; ];
/** /**

View File

@ -19,8 +19,9 @@ class CreateGoalsTable extends Migration
$table->foreignIdFor(User::class)->constrained()->cascadeOnUpdate()->cascadeOnDelete(); $table->foreignIdFor(User::class)->constrained()->cascadeOnUpdate()->cascadeOnDelete();
$table->date('from')->nullable(); $table->date('from')->nullable();
$table->date('to')->nullable(); $table->date('to')->nullable();
$table->string('goal'); $table->string('frequency')->nullable();
$table->unsignedFloat('amount'); $table->string('name');
$table->unsignedFloat('goal');
$table->timestamps(); $table->timestamps();
$table->index('user_id'); $table->index('user_id');
}); });

View File

@ -13,7 +13,7 @@
@method('delete') @method('delete')
@csrf @csrf
<div class="text-lg pb-3"> <div class="text-lg pb-3">
Are you sure what to delete your <span class="font-extrabold">{{ $goalOptions[$goal->goal]['label'] }}</span> goal? Are you sure what to delete your <span class="font-extrabold">{{ $goal->summary }}</span> goal?
</div> </div>
<x-inputs.button class="bg-red-800 hover:bg-red-700"> <x-inputs.button class="bg-red-800 hover:bg-red-700">
Yes, delete Yes, delete

View File

@ -32,24 +32,36 @@
:value="old('to', $goal->to)" /> :value="old('to', $goal->to)" />
</div> </div>
<!-- Amount --> <!-- Frequency -->
<div class="flex-auto"> <div class="flex-auto">
<x-inputs.label for="amount" value="Amount" /> <x-inputs.label for="frequency" value="Frequency" />
<x-inputs.input name="amount" <x-inputs.select name="frequency"
type="number" class="block w-full"
step="any" :options="$frequencyOptions"
class="block w-full" :selectedValue="old('frequency', $goal->frequency)">
:value="old('amount', $goal->amount)"/> </x-inputs.select>
</div>
<!-- Name -->
<div class="flex-auto">
<x-inputs.label for="name" value="Trackable" />
<x-inputs.select name="name"
class="block w-full"
:options="$nameOptions"
:selectedValue="old('name', $goal->name)"
required>
</x-inputs.select>
</div> </div>
<!-- Goal --> <!-- Goal -->
<div class="flex-auto"> <div class="flex-auto">
<x-inputs.label for="goal" value="Goal" /> <x-inputs.label for="goal" value="Goal" />
<x-inputs.select name="goal" <x-inputs.input name="goal"
class="block w-full" type="number"
:options="$goalOptions" step="any"
:selectedValue="old('goal', $goal->goal)"> class="block w-full"
</x-inputs.select> :value="old('goal', $goal->goal)"
required />
</div> </div>
</div> </div>
</div> </div>

View File

@ -36,10 +36,9 @@
<div class="max-w-7xl mx-auto sm:px-6 lg:px-8"> <div class="max-w-7xl mx-auto sm:px-6 lg:px-8">
<div class="bg-white overflow-hidden shadow-sm sm:rounded-lg"> <div class="bg-white overflow-hidden shadow-sm sm:rounded-lg">
<div class="p-6 bg-white border-b border-gray-200"> <div class="p-6 bg-white border-b border-gray-200">
<h3 class="text-xl font-semibold text-gray-800">Goals</h3>
@forelse($goals['present'] as $goal) @forelse($goals['present'] as $goal)
<details> <details>
<summary>{{ number_format($goal->amount, 0) }}{{ $goalOptions[$goal->goal]['unit'] }} {{ $goalOptions[$goal->goal]['label'] }}</summary> <summary>{{ $goal->summary }}</summary>
TODO: Details for the day! TODO: Details for the day!
<div class="flex"> <div class="flex">
<a class="text-gray-500 hover:text-gray-700 hover:border-gray-300 text-sm" <a class="text-gray-500 hover:text-gray-700 hover:border-gray-300 text-sm"

View File

@ -1,7 +1,7 @@
<x-app-layout> <x-app-layout>
<x-slot name="header"> <x-slot name="header">
<h2 class="font-semibold text-xl text-gray-800 leading-tight flex flex-auto"> <h2 class="font-semibold text-xl text-gray-800 leading-tight flex flex-auto">
TODO: GOAL NAME {{ $goal->summary }}
<a class="ml-2 text-gray-500 hover:text-gray-700 hover:border-gray-300 text-sm" <a class="ml-2 text-gray-500 hover:text-gray-700 hover:border-gray-300 text-sm"
href="{{ route('goals.edit', $goal) }}"> href="{{ route('goals.edit', $goal) }}">
<svg class="h-6 w-6" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 20 20" fill="currentColor"> <svg class="h-6 w-6" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 20 20" fill="currentColor">