mirror of https://github.com/kcal-app/kcal.git
Merge remote-tracking branch 'origin/main' into demo
This commit is contained in:
commit
741de17e07
|
|
@ -1,10 +1,11 @@
|
|||
# Local env file assumes Sail is in use. See docker-compose.yml.
|
||||
APP_NAME=kcal
|
||||
APP_ENV=local
|
||||
APP_KEY=
|
||||
APP_DEBUG=true
|
||||
APP_URL=http://127.0.0.1
|
||||
APP_URL=http://kcal.test
|
||||
APP_PORT=8080
|
||||
APP_SERVICE=kcal.test
|
||||
APP_SERVICE=app
|
||||
APP_TIMEZONE=UTC
|
||||
|
||||
LOG_CHANNEL=stack
|
||||
|
|
@ -17,17 +18,8 @@ DB_DATABASE=kcal
|
|||
DB_USERNAME=kcal
|
||||
DB_PASSWORD=kcal
|
||||
|
||||
#REDIS_URL=
|
||||
REDIS_HOST=redis
|
||||
#REDIS_PASSWORD=
|
||||
REDIS_PORT=6379
|
||||
#REDIS_DB=
|
||||
|
||||
#SCOUT_DRIVER=null
|
||||
|
||||
#SCOUT_DRIVER=algolia
|
||||
#ALGOLIA_APP_ID=
|
||||
#ALGOLIA_SECRET=
|
||||
|
||||
SCOUT_DRIVER=elastic
|
||||
ELASTIC_HOST=elasticsearch:9200
|
||||
|
|
|
|||
|
|
@ -35,7 +35,7 @@ jobs:
|
|||
run: composer install --no-progress --no-interaction
|
||||
- name: Generate app key
|
||||
run: |
|
||||
php -r "file_exists('.env') || copy('.env.ci.example', '.env');"
|
||||
php -r "file_exists('.env') || copy('.env.ci', '.env');"
|
||||
php artisan key:generate
|
||||
- name: Run tests
|
||||
run: vendor/bin/paratest --coverage-clover build/logs/clover.xml
|
||||
|
|
|
|||
|
|
@ -3,7 +3,7 @@
|
|||
|
||||
/**
|
||||
* A helper file for Laravel, to provide autocomplete information to your IDE
|
||||
* Generated for Laravel 8.35.1.
|
||||
* Generated for Laravel 8.36.2.
|
||||
*
|
||||
* This file should not be included in your code, only analyzed by your IDE!
|
||||
*
|
||||
|
|
@ -2773,7 +2773,7 @@
|
|||
/**
|
||||
* Dispatch a command to its appropriate handler in the current process.
|
||||
*
|
||||
* Queuable jobs will be dispatched to the "sync" queue.
|
||||
* Queueable jobs will be dispatched to the "sync" queue.
|
||||
*
|
||||
* @param mixed $command
|
||||
* @param mixed $handler
|
||||
|
|
@ -15217,6 +15217,16 @@
|
|||
*
|
||||
* @static
|
||||
*/
|
||||
public static function censorRequestBodyFields($fieldNames)
|
||||
{
|
||||
/** @var \Facade\FlareClient\Flare $instance */
|
||||
return $instance->censorRequestBodyFields($fieldNames);
|
||||
}
|
||||
/**
|
||||
*
|
||||
*
|
||||
* @static
|
||||
*/
|
||||
public static function createReport($throwable)
|
||||
{
|
||||
/** @var \Facade\FlareClient\Flare $instance */
|
||||
|
|
|
|||
|
|
@ -20,6 +20,8 @@ use Illuminate\Http\RedirectResponse;
|
|||
use Illuminate\Http\Request;
|
||||
use Illuminate\Support\Carbon;
|
||||
use Illuminate\Support\Facades\Auth;
|
||||
use Illuminate\Support\Pluralizer;
|
||||
use Illuminate\Support\Str;
|
||||
|
||||
class JournalEntryController extends Controller
|
||||
{
|
||||
|
|
@ -156,72 +158,56 @@ class JournalEntryController extends Controller
|
|||
/** @var \App\Models\JournalEntry[] $entries */
|
||||
$entries = [];
|
||||
$entry_key = 0;
|
||||
$group_entries = isset($input['group_entries']) && (bool) $input['group_entries'];
|
||||
// TODO: Improve efficiency. Potential for lots of queries here...
|
||||
foreach ($ingredients as $ingredient) {
|
||||
// Set entry key (combined date and meal or individual entries).
|
||||
if (isset($input['group_entries']) && (bool) $input['group_entries']) {
|
||||
if ($group_entries) {
|
||||
$entry_key = "{$ingredient['date']}{$ingredient['meal']}";
|
||||
}
|
||||
else {
|
||||
$entry_key++;
|
||||
}
|
||||
|
||||
// Prepare entry values.
|
||||
// Get an existing entry (when grouping) or create a new one.
|
||||
$entries[$entry_key] = $entries[$entry_key] ?? JournalEntry::make([
|
||||
'date' => $ingredient['date'],
|
||||
'meal' => $ingredient['meal'],
|
||||
])->user()->associate(Auth::user());
|
||||
$entry = &$entries[$entry_key];
|
||||
|
||||
// Calculate amounts based on ingredient type.
|
||||
$item = NULL;
|
||||
$amount = Number::floatFromString($ingredient['amount']);
|
||||
if ($ingredient['type'] == Food::class) {
|
||||
$item = Food::whereId($ingredient['id'])->first();
|
||||
$nutrient_multiplier = Nutrients::calculateFoodNutrientMultiplier(
|
||||
$item,
|
||||
Number::floatFromString($ingredient['amount']),
|
||||
$ingredient['unit']
|
||||
);
|
||||
$nutrient_multiplier = Nutrients::calculateFoodNutrientMultiplier($item, $amount, $ingredient['unit']);
|
||||
foreach (Nutrients::all()->pluck('value') as $nutrient) {
|
||||
$entries[$entry_key]->{$nutrient} += $item->{$nutrient} * $nutrient_multiplier;
|
||||
$entry->{$nutrient} += $item->{$nutrient} * $nutrient_multiplier;
|
||||
}
|
||||
$entries[$entry_key]->foods->add($item);
|
||||
$entry->foods->add($item);
|
||||
}
|
||||
elseif ($ingredient['type'] == Recipe::class) {
|
||||
$item = Recipe::whereId($ingredient['id'])->first();
|
||||
foreach (Nutrients::all()->pluck('value') as $nutrient) {
|
||||
$entries[$entry_key]->{$nutrient} += Nutrients::calculateRecipeNutrientAmount(
|
||||
$item,
|
||||
$nutrient,
|
||||
Number::floatFromString($ingredient['amount']),
|
||||
$ingredient['unit']
|
||||
);
|
||||
$entry->{$nutrient} += Nutrients::calculateRecipeNutrientAmount($item, $nutrient, $amount, $ingredient['unit']);
|
||||
}
|
||||
$entries[$entry_key]->recipes->add($item);
|
||||
}
|
||||
else {
|
||||
return back()->withInput()->withErrors("Invalid ingredient type {$ingredient['type']}.");
|
||||
$entry->recipes->add($item);
|
||||
}
|
||||
|
||||
// Set entry summary.
|
||||
$unit = $ingredient['unit'];
|
||||
if ($item instanceof Food) {
|
||||
if ($unit === 'serving') {
|
||||
if (empty($item->serving_unit) && empty($item->serving_unit_name)) {
|
||||
$unit = null;
|
||||
}
|
||||
elseif (!empty($item->serving_unit_name)) {
|
||||
$unit = $item->serving_unit_formatted;
|
||||
}
|
||||
}
|
||||
// Add to summary.
|
||||
if (!empty($entry->summary)) {
|
||||
$entry->summary .= '; ';
|
||||
}
|
||||
$entries[$entry_key]->summary .= (!empty($entries[$entry_key]->summary) ? ', ' : null);
|
||||
$entries[$entry_key]->summary .= "{$ingredient['amount']} {$unit} {$item->name}";
|
||||
$entry->summary .= $this->createIngredientSummary($ingredient, $item, $amount);
|
||||
}
|
||||
|
||||
foreach ($entries as $entry) {
|
||||
$entry->save();
|
||||
$entry->user->save();
|
||||
$entry->foods()->saveMany($entry->foods);
|
||||
$entry->recipes()->saveMany($entry->recipes);
|
||||
// Save all new entries.
|
||||
foreach ($entries as $new_entry) {
|
||||
$new_entry->save();
|
||||
$new_entry->user->save();
|
||||
$new_entry->foods()->saveMany($new_entry->foods);
|
||||
$new_entry->recipes()->saveMany($new_entry->recipes);
|
||||
}
|
||||
|
||||
$count = count($entries);
|
||||
|
|
@ -236,6 +222,65 @@ class JournalEntryController extends Controller
|
|||
return redirect()->route('journal-entries.index', $parameters);
|
||||
}
|
||||
|
||||
/**
|
||||
* Attempt to create a coherent summary for an entry ingredient.
|
||||
*/
|
||||
private function createIngredientSummary(array $ingredient, Food|Recipe $item, float $amount): string {
|
||||
$name = $item->name;
|
||||
$unit = $ingredient['unit'];
|
||||
|
||||
// Determine unit with special handling for custom Food units.
|
||||
if ($item instanceof Food) {
|
||||
if ($unit === 'serving') {
|
||||
$no_serving_unit = empty($item->serving_unit) && empty($item->serving_unit_name);
|
||||
|
||||
// If there is no serving unit or the serving unit name is
|
||||
// exactly the same as the item name don't use a serving
|
||||
// unit and pluralize the _item_ name.
|
||||
if ($no_serving_unit || $item->serving_unit_name === $name) {
|
||||
$unit = null;
|
||||
$name = Pluralizer::plural($name, $amount);
|
||||
}
|
||||
|
||||
// If the serving unit name is already _part_ of the item
|
||||
// name, just keep the defined unit (e.g. name: "tortilla
|
||||
// chips" and serving name "chips").
|
||||
elseif (Str::contains($name, $item->serving_unit_name)) {
|
||||
$unit = 'serving';
|
||||
}
|
||||
|
||||
// If a serving unit name is set, use the formatted serving
|
||||
// unit name as a base.
|
||||
elseif (!empty($item->serving_unit_name)) {
|
||||
$unit = $item->serving_unit_formatted;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Pluralize unit with supplied plurals or Pluralizer.
|
||||
if (Nutrients::units()->has($unit)) {
|
||||
$value = 'label';
|
||||
if ($amount > 1) {
|
||||
$value = 'plural';
|
||||
}
|
||||
$unit = Nutrients::units()->get($unit)[$value];
|
||||
}
|
||||
else {
|
||||
$unit = Pluralizer::plural($unit, $amount);
|
||||
}
|
||||
|
||||
// Add amount, unit, and name to summary.
|
||||
$amount = Number::rationalStringFromFloat($amount);
|
||||
$summary = "{$amount} {$unit} {$name}";
|
||||
|
||||
// Add detail if available.
|
||||
if (isset($item->detail) && !empty($item->detail)) {
|
||||
$summary .= ", {$item->detail}";
|
||||
}
|
||||
|
||||
return $summary;
|
||||
}
|
||||
|
||||
/**
|
||||
* Store an entry from nutrients.
|
||||
*/
|
||||
|
|
|
|||
|
|
@ -170,7 +170,7 @@ final class Food extends Model
|
|||
public function getServingSizeFormattedAttribute(): ?string {
|
||||
$result = null;
|
||||
if (!empty($this->serving_size)) {
|
||||
$result = Number::fractionStringFromFloat($this->serving_size);
|
||||
$result = Number::rationalStringFromFloat($this->serving_size);
|
||||
}
|
||||
return $result;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -92,7 +92,7 @@ final class IngredientAmount extends Model
|
|||
* Get the amount as a formatted string (e.g. 0.5 = 1/2).
|
||||
*/
|
||||
public function getAmountFormattedAttribute(): string {
|
||||
return Number::fractionStringFromFloat($this->amount);
|
||||
return Number::rationalStringFromFloat($this->amount);
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
@ -103,7 +103,7 @@ final class IngredientAmount extends Model
|
|||
public function getNutrientsSummaryAttribute(): string {
|
||||
$summary = [];
|
||||
foreach (Nutrients::all() as $nutrient) {
|
||||
$amount = round($this->{$nutrient['value']}(), 2);
|
||||
$amount = Nutrients::round($this->{$nutrient['value']}(), $nutrient['value']);
|
||||
$summary[] = "{$nutrient['label']}: {$amount}{$nutrient['unit']}";
|
||||
}
|
||||
return implode(', ', $summary);
|
||||
|
|
|
|||
|
|
@ -7,6 +7,7 @@ use App\Models\Traits\Ingredient;
|
|||
use App\Models\Traits\Journalable;
|
||||
use App\Models\Traits\Sluggable;
|
||||
use App\Models\Traits\Taggable;
|
||||
use App\Support\Nutrients;
|
||||
use ElasticScoutDriverPlus\QueryDsl;
|
||||
use Illuminate\Database\Eloquent\Factories\HasFactory;
|
||||
use Illuminate\Database\Eloquent\Model;
|
||||
|
|
@ -165,7 +166,7 @@ final class Recipe extends Model implements HasMedia
|
|||
if (empty($this->weight)) {
|
||||
return null;
|
||||
}
|
||||
return round($this->weight / $this->servings, 2);
|
||||
return round($this->weight / $this->servings);
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
@ -209,13 +210,6 @@ final class Recipe extends Model implements HasMedia
|
|||
|
||||
/**
|
||||
* Add nutrient calculations handling to overloading.
|
||||
*
|
||||
* @param string $method
|
||||
* @param array $parameters
|
||||
*
|
||||
* @return mixed
|
||||
*
|
||||
* @noinspection PhpMissingParamTypeInspection
|
||||
*/
|
||||
public function __call($method, $parameters): mixed {
|
||||
if (in_array($method, $this->nutrientTotalMethods)) {
|
||||
|
|
@ -223,15 +217,7 @@ final class Recipe extends Model implements HasMedia
|
|||
}
|
||||
elseif (in_array($method, $this->nutrientPerServingMethods)) {
|
||||
$sum = $this->sumNutrient(substr($method, 0, -10)) / $this->servings;
|
||||
|
||||
// Per-serving calculations are rounded, though actual food label
|
||||
// rounding standards are more complex.
|
||||
if ($sum > 1) {
|
||||
return round($sum);
|
||||
}
|
||||
else {
|
||||
return round($sum, 2);
|
||||
}
|
||||
return Nutrients::round($sum, substr($method, 0, -10));
|
||||
}
|
||||
else {
|
||||
return parent::__call($method, $parameters);
|
||||
|
|
|
|||
|
|
@ -7,10 +7,10 @@ use Phospr\Fraction;
|
|||
class Number
|
||||
{
|
||||
/**
|
||||
* Get a float value from a decimal or fraction string.
|
||||
* Get a float value from a decimal or rational string.
|
||||
*
|
||||
* @param string $value
|
||||
* Value in decimal or string format.
|
||||
* Decimal or rational string.
|
||||
* @return float
|
||||
* Float representation of the value.
|
||||
*
|
||||
|
|
@ -27,21 +27,38 @@ class Number
|
|||
}
|
||||
|
||||
/**
|
||||
* Get a string faction representation of a float.
|
||||
* Get a string rational representation of a float.
|
||||
*
|
||||
* @todo Handle repeating values like 1/3, 2/3, etc. better.
|
||||
*
|
||||
* @see https://rosettacode.org/wiki/Convert_decimal_number_to_rational#PHP
|
||||
* Special handling is used for common cases 1/3 and 2/3 to ensure the
|
||||
* expected rationals. Other less common rationals (e.g. n/7 or n/9) will
|
||||
* not be well handled here.
|
||||
*
|
||||
* @param float $value
|
||||
* Value to convert to string fraction.
|
||||
* Value to convert to rational string.
|
||||
* @return string
|
||||
* String fraction.
|
||||
* Rational string.
|
||||
*
|
||||
* @todo Learn maths.
|
||||
*/
|
||||
public static function fractionStringFromFloat(float $value): string {
|
||||
$fraction = (string) Fraction::fromFloat($value);
|
||||
$fraction = str_replace(['33/100', '33333333/100000000'], '1/3', $fraction);
|
||||
$fraction = str_replace(['67/100', '66666667/100000000'], '2/3', $fraction);
|
||||
return $fraction;
|
||||
public static function rationalStringFromFloat(float $value): string {
|
||||
$decimal = Fraction::fromFloat(($value - floor($value)));
|
||||
if ($decimal->isSameValueAs(Fraction::fromFloat(1/3))) {
|
||||
$string = '1/3';
|
||||
$whole = floor($value);
|
||||
if ($whole > 0) {
|
||||
$string = "{$whole} {$string}";
|
||||
}
|
||||
}
|
||||
elseif ($decimal->isSameValueAs(Fraction::fromFloat(2/3))) {
|
||||
$string = '2/3';
|
||||
$whole = floor($value);
|
||||
if ($whole > 0) {
|
||||
$string = "{$whole} {$string}";
|
||||
}
|
||||
}
|
||||
else {
|
||||
$string = (string) Fraction::fromFloat($value);
|
||||
}
|
||||
return $string;
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -16,6 +16,7 @@ class Nutrients
|
|||
* Each entry has two keys:
|
||||
* - value: Machine name for the unit.
|
||||
* - label: Human-readable name for the unit.
|
||||
* - plural: Human-readable plural form of the unit name.
|
||||
* - type: Unit type -- matching types can be converted.
|
||||
*
|
||||
* @return \Illuminate\Support\Collection
|
||||
|
|
@ -25,31 +26,37 @@ class Nutrients
|
|||
'cup' => [
|
||||
'value' => 'cup',
|
||||
'label' => 'cup',
|
||||
'plural' => 'cups',
|
||||
'type' => 'volume',
|
||||
],
|
||||
'gram' => [
|
||||
'value' => 'gram',
|
||||
'label' => 'gram',
|
||||
'plural' => 'grams',
|
||||
'type' => 'weight',
|
||||
],
|
||||
'oz' => [
|
||||
'value' => 'oz',
|
||||
'label' => 'oz',
|
||||
'plural' => 'oz',
|
||||
'type' => 'weight',
|
||||
],
|
||||
'serving' => [
|
||||
'value' => 'serving',
|
||||
'label' => 'serving',
|
||||
'plural' => 'servings',
|
||||
'type' => 'division',
|
||||
],
|
||||
'tbsp' => [
|
||||
'value' => 'tbsp',
|
||||
'label' => 'tbsp.',
|
||||
'plural' => 'tbsp.',
|
||||
'type' => 'volume',
|
||||
],
|
||||
'tsp' => [
|
||||
'value' => 'tsp',
|
||||
'label' => 'tsp.',
|
||||
'plural' => 'tsp.',
|
||||
'type' => 'volume',
|
||||
],
|
||||
]);
|
||||
|
|
@ -188,4 +195,57 @@ class Nutrients
|
|||
throw new \DomainException("Unsupported recipe unit: {$fromUnit}");
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Round a nutrient amount according to FDA guidelines.
|
||||
*
|
||||
* Note: this stays mostly true to the guidelines except that carbohydrates
|
||||
* and protein are meant to state "less than 1 gram" when the amount is less
|
||||
* than 1 gram. Instead, this method treats anything less than 1 gram as
|
||||
* zero.
|
||||
*
|
||||
* @url https://labelcalc.com/food-labeling/a-guide-to-using-fda-rounding-rules-for-your-food-label/
|
||||
*
|
||||
* @throws \InvalidArgumentException
|
||||
*/
|
||||
public static function round(float $amount, string $nutrient): float {
|
||||
return match ($nutrient) {
|
||||
|
||||
/*
|
||||
* Calories:
|
||||
* - Less than 5 goes to zero.
|
||||
* - Between 5 and 50 rounds to nearest number divisible by 5.
|
||||
* - Greater than 50 rounds to nearest number divisible by 10.
|
||||
*/
|
||||
'calories' => ($amount < 5 ? 0 : ($amount <= 50 ? round($amount / 5 ) * 5 : round($amount / 10 ) * 10)),
|
||||
|
||||
/*
|
||||
* Carbohydrates and protein:
|
||||
* - Less than 1 goes to zero.
|
||||
* - Greater than 1 rounds to nearest whole.
|
||||
*/
|
||||
'carbohydrates', 'protein' => ($amount < 1 ? 0 : round($amount)),
|
||||
|
||||
/*
|
||||
* Cholesterol and fat:
|
||||
* - Less than 0.5 goes to zero.
|
||||
* - Between 0.5 and 5 rounds to nearest half.
|
||||
* - Greater than 5 rounds to nearest whole.
|
||||
*/
|
||||
'cholesterol', 'fat' => ($amount < 0.5 ? 0 : ($amount <= 5 ? round($amount / 5, 1 ) * 5 : round($amount))),
|
||||
|
||||
/*
|
||||
* Sodium:
|
||||
* - Less than 5 goes to zero.
|
||||
* - Between 5 and 140 rounds to nearest number divisible by 5.
|
||||
* - Greater than 140 rounds to nearest number divisible by 10.
|
||||
*/
|
||||
'sodium' => ($amount < 5 ? 0 : ($amount <= 140 ? round($amount / 5 ) * 5 : round($amount / 10 ) * 10)),
|
||||
|
||||
/*
|
||||
* Anything else excepts!
|
||||
*/
|
||||
default => throw new \InvalidArgumentException("Unrecognized nutrient {$nutrient}.")
|
||||
};
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -8,16 +8,16 @@
|
|||
"packages": [
|
||||
{
|
||||
"name": "algolia/algoliasearch-client-php",
|
||||
"version": "2.7.3",
|
||||
"version": "2.8.0",
|
||||
"source": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/algolia/algoliasearch-client-php.git",
|
||||
"reference": "142a382e4649db0cb64d9eb8893872f1a4ba8dd3"
|
||||
"reference": "d9781147ae433f5bdbfd902497d748d60e70d693"
|
||||
},
|
||||
"dist": {
|
||||
"type": "zip",
|
||||
"url": "https://api.github.com/repos/algolia/algoliasearch-client-php/zipball/142a382e4649db0cb64d9eb8893872f1a4ba8dd3",
|
||||
"reference": "142a382e4649db0cb64d9eb8893872f1a4ba8dd3",
|
||||
"url": "https://api.github.com/repos/algolia/algoliasearch-client-php/zipball/d9781147ae433f5bdbfd902497d748d60e70d693",
|
||||
"reference": "d9781147ae433f5bdbfd902497d748d60e70d693",
|
||||
"shasum": ""
|
||||
},
|
||||
"require": {
|
||||
|
|
@ -76,22 +76,22 @@
|
|||
],
|
||||
"support": {
|
||||
"issues": "https://github.com/algolia/algoliasearch-client-php/issues",
|
||||
"source": "https://github.com/algolia/algoliasearch-client-php/tree/2.7.3"
|
||||
"source": "https://github.com/algolia/algoliasearch-client-php/tree/2.8.0"
|
||||
},
|
||||
"time": "2020-12-22T11:27:03+00:00"
|
||||
"time": "2021-04-07T16:50:58+00:00"
|
||||
},
|
||||
{
|
||||
"name": "algolia/scout-extended",
|
||||
"version": "v1.15.0",
|
||||
"version": "v1.16.0",
|
||||
"source": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/algolia/scout-extended.git",
|
||||
"reference": "f1da27101b4c88166f9d66d5110b46e1dacb8f1c"
|
||||
"reference": "1154a57e8049e7d07d51571599ee5aedd106b945"
|
||||
},
|
||||
"dist": {
|
||||
"type": "zip",
|
||||
"url": "https://api.github.com/repos/algolia/scout-extended/zipball/f1da27101b4c88166f9d66d5110b46e1dacb8f1c",
|
||||
"reference": "f1da27101b4c88166f9d66d5110b46e1dacb8f1c",
|
||||
"url": "https://api.github.com/repos/algolia/scout-extended/zipball/1154a57e8049e7d07d51571599ee5aedd106b945",
|
||||
"reference": "1154a57e8049e7d07d51571599ee5aedd106b945",
|
||||
"shasum": ""
|
||||
},
|
||||
"require": {
|
||||
|
|
@ -157,9 +157,9 @@
|
|||
],
|
||||
"support": {
|
||||
"issues": "https://github.com/algolia/scout-extended/issues",
|
||||
"source": "https://github.com/algolia/scout-extended/tree/v1.15.0"
|
||||
"source": "https://github.com/algolia/scout-extended/tree/v1.16.0"
|
||||
},
|
||||
"time": "2021-03-17T15:52:17+00:00"
|
||||
"time": "2021-04-08T15:17:35+00:00"
|
||||
},
|
||||
{
|
||||
"name": "asm89/stack-cors",
|
||||
|
|
@ -2336,16 +2336,16 @@
|
|||
},
|
||||
{
|
||||
"name": "laravel/framework",
|
||||
"version": "v8.35.1",
|
||||
"version": "v8.36.2",
|
||||
"source": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/laravel/framework.git",
|
||||
"reference": "d118c0df39e7524131176aaf76493eae63a8a602"
|
||||
"reference": "0debd8ad6b5aa1f61ccc73910adf049af4ca0444"
|
||||
},
|
||||
"dist": {
|
||||
"type": "zip",
|
||||
"url": "https://api.github.com/repos/laravel/framework/zipball/d118c0df39e7524131176aaf76493eae63a8a602",
|
||||
"reference": "d118c0df39e7524131176aaf76493eae63a8a602",
|
||||
"url": "https://api.github.com/repos/laravel/framework/zipball/0debd8ad6b5aa1f61ccc73910adf049af4ca0444",
|
||||
"reference": "0debd8ad6b5aa1f61ccc73910adf049af4ca0444",
|
||||
"shasum": ""
|
||||
},
|
||||
"require": {
|
||||
|
|
@ -2500,20 +2500,20 @@
|
|||
"issues": "https://github.com/laravel/framework/issues",
|
||||
"source": "https://github.com/laravel/framework"
|
||||
},
|
||||
"time": "2021-03-30T21:34:17+00:00"
|
||||
"time": "2021-04-07T12:37:22+00:00"
|
||||
},
|
||||
{
|
||||
"name": "laravel/scout",
|
||||
"version": "v8.6.0",
|
||||
"version": "v8.6.1",
|
||||
"source": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/laravel/scout.git",
|
||||
"reference": "54070f7b68fed15f25e61e68884c4110496b8aa1"
|
||||
"reference": "7fb1c860a2fd904f0e084a7cc3641eb1448ba278"
|
||||
},
|
||||
"dist": {
|
||||
"type": "zip",
|
||||
"url": "https://api.github.com/repos/laravel/scout/zipball/54070f7b68fed15f25e61e68884c4110496b8aa1",
|
||||
"reference": "54070f7b68fed15f25e61e68884c4110496b8aa1",
|
||||
"url": "https://api.github.com/repos/laravel/scout/zipball/7fb1c860a2fd904f0e084a7cc3641eb1448ba278",
|
||||
"reference": "7fb1c860a2fd904f0e084a7cc3641eb1448ba278",
|
||||
"shasum": ""
|
||||
},
|
||||
"require": {
|
||||
|
|
@ -2569,7 +2569,7 @@
|
|||
"issues": "https://github.com/laravel/scout/issues",
|
||||
"source": "https://github.com/laravel/scout"
|
||||
},
|
||||
"time": "2021-01-19T15:30:52+00:00"
|
||||
"time": "2021-04-06T14:35:41+00:00"
|
||||
},
|
||||
{
|
||||
"name": "laravel/tinker",
|
||||
|
|
@ -3470,16 +3470,16 @@
|
|||
},
|
||||
{
|
||||
"name": "opis/closure",
|
||||
"version": "3.6.1",
|
||||
"version": "3.6.2",
|
||||
"source": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/opis/closure.git",
|
||||
"reference": "943b5d70cc5ae7483f6aff6ff43d7e34592ca0f5"
|
||||
"reference": "06e2ebd25f2869e54a306dda991f7db58066f7f6"
|
||||
},
|
||||
"dist": {
|
||||
"type": "zip",
|
||||
"url": "https://api.github.com/repos/opis/closure/zipball/943b5d70cc5ae7483f6aff6ff43d7e34592ca0f5",
|
||||
"reference": "943b5d70cc5ae7483f6aff6ff43d7e34592ca0f5",
|
||||
"url": "https://api.github.com/repos/opis/closure/zipball/06e2ebd25f2869e54a306dda991f7db58066f7f6",
|
||||
"reference": "06e2ebd25f2869e54a306dda991f7db58066f7f6",
|
||||
"shasum": ""
|
||||
},
|
||||
"require": {
|
||||
|
|
@ -3529,9 +3529,9 @@
|
|||
],
|
||||
"support": {
|
||||
"issues": "https://github.com/opis/closure/issues",
|
||||
"source": "https://github.com/opis/closure/tree/3.6.1"
|
||||
"source": "https://github.com/opis/closure/tree/3.6.2"
|
||||
},
|
||||
"time": "2020-11-07T02:01:34+00:00"
|
||||
"time": "2021-04-09T13:42:10+00:00"
|
||||
},
|
||||
{
|
||||
"name": "phospr/fraction",
|
||||
|
|
@ -4522,16 +4522,16 @@
|
|||
},
|
||||
{
|
||||
"name": "spatie/image",
|
||||
"version": "1.10.4",
|
||||
"version": "1.10.5",
|
||||
"source": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/spatie/image.git",
|
||||
"reference": "7ea129bc7b7521864c5a540e3b1c14ea194316d3"
|
||||
"reference": "63a963d0200fb26f2564bf7201fc7272d9b22933"
|
||||
},
|
||||
"dist": {
|
||||
"type": "zip",
|
||||
"url": "https://api.github.com/repos/spatie/image/zipball/7ea129bc7b7521864c5a540e3b1c14ea194316d3",
|
||||
"reference": "7ea129bc7b7521864c5a540e3b1c14ea194316d3",
|
||||
"url": "https://api.github.com/repos/spatie/image/zipball/63a963d0200fb26f2564bf7201fc7272d9b22933",
|
||||
"reference": "63a963d0200fb26f2564bf7201fc7272d9b22933",
|
||||
"shasum": ""
|
||||
},
|
||||
"require": {
|
||||
|
|
@ -4541,7 +4541,7 @@
|
|||
"league/glide": "^1.6",
|
||||
"php": "^7.2|^8.0",
|
||||
"spatie/image-optimizer": "^1.1",
|
||||
"spatie/temporary-directory": "^1.0",
|
||||
"spatie/temporary-directory": "^1.0|^2.0",
|
||||
"symfony/process": "^3.0|^4.0|^5.0"
|
||||
},
|
||||
"require-dev": {
|
||||
|
|
@ -4575,7 +4575,7 @@
|
|||
],
|
||||
"support": {
|
||||
"issues": "https://github.com/spatie/image/issues",
|
||||
"source": "https://github.com/spatie/image/tree/1.10.4"
|
||||
"source": "https://github.com/spatie/image/tree/1.10.5"
|
||||
},
|
||||
"funding": [
|
||||
{
|
||||
|
|
@ -4587,7 +4587,7 @@
|
|||
"type": "github"
|
||||
}
|
||||
],
|
||||
"time": "2021-03-10T16:11:40+00:00"
|
||||
"time": "2021-04-07T08:42:24+00:00"
|
||||
},
|
||||
{
|
||||
"name": "spatie/image-optimizer",
|
||||
|
|
@ -4645,16 +4645,16 @@
|
|||
},
|
||||
{
|
||||
"name": "spatie/laravel-medialibrary",
|
||||
"version": "9.5.0",
|
||||
"version": "9.5.3",
|
||||
"source": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/spatie/laravel-medialibrary.git",
|
||||
"reference": "89d1d2e5b4b53137819cc18a166f43edaeaf7e52"
|
||||
"reference": "ebbc996db457adecc778db6030d22ef72b495d59"
|
||||
},
|
||||
"dist": {
|
||||
"type": "zip",
|
||||
"url": "https://api.github.com/repos/spatie/laravel-medialibrary/zipball/89d1d2e5b4b53137819cc18a166f43edaeaf7e52",
|
||||
"reference": "89d1d2e5b4b53137819cc18a166f43edaeaf7e52",
|
||||
"url": "https://api.github.com/repos/spatie/laravel-medialibrary/zipball/ebbc996db457adecc778db6030d22ef72b495d59",
|
||||
"reference": "ebbc996db457adecc778db6030d22ef72b495d59",
|
||||
"shasum": ""
|
||||
},
|
||||
"require": {
|
||||
|
|
@ -4670,7 +4670,7 @@
|
|||
"maennchen/zipstream-php": "^1.0|^2.0",
|
||||
"php": "^7.4|^8.0",
|
||||
"spatie/image": "^1.4.0",
|
||||
"spatie/temporary-directory": "^1.1",
|
||||
"spatie/temporary-directory": "^1.1|^2.0",
|
||||
"symfony/console": "^4.4|^5.0"
|
||||
},
|
||||
"conflict": {
|
||||
|
|
@ -4733,7 +4733,7 @@
|
|||
],
|
||||
"support": {
|
||||
"issues": "https://github.com/spatie/laravel-medialibrary/issues",
|
||||
"source": "https://github.com/spatie/laravel-medialibrary/tree/9.5.0"
|
||||
"source": "https://github.com/spatie/laravel-medialibrary/tree/9.5.3"
|
||||
},
|
||||
"funding": [
|
||||
{
|
||||
|
|
@ -4745,7 +4745,7 @@
|
|||
"type": "github"
|
||||
}
|
||||
],
|
||||
"time": "2021-03-29T21:40:29+00:00"
|
||||
"time": "2021-04-08T08:27:49+00:00"
|
||||
},
|
||||
{
|
||||
"name": "spatie/laravel-tags",
|
||||
|
|
@ -4899,23 +4899,23 @@
|
|||
},
|
||||
{
|
||||
"name": "spatie/temporary-directory",
|
||||
"version": "1.3.0",
|
||||
"version": "2.0.0",
|
||||
"source": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/spatie/temporary-directory.git",
|
||||
"reference": "f517729b3793bca58f847c5fd383ec16f03ffec6"
|
||||
"reference": "06fe0f10d068fdf145c9b2235030e568c913bb61"
|
||||
},
|
||||
"dist": {
|
||||
"type": "zip",
|
||||
"url": "https://api.github.com/repos/spatie/temporary-directory/zipball/f517729b3793bca58f847c5fd383ec16f03ffec6",
|
||||
"reference": "f517729b3793bca58f847c5fd383ec16f03ffec6",
|
||||
"url": "https://api.github.com/repos/spatie/temporary-directory/zipball/06fe0f10d068fdf145c9b2235030e568c913bb61",
|
||||
"reference": "06fe0f10d068fdf145c9b2235030e568c913bb61",
|
||||
"shasum": ""
|
||||
},
|
||||
"require": {
|
||||
"php": "^7.2|^8.0"
|
||||
"php": "^8.0"
|
||||
},
|
||||
"require-dev": {
|
||||
"phpunit/phpunit": "^8.0|^9.0"
|
||||
"phpunit/phpunit": "^9.5"
|
||||
},
|
||||
"type": "library",
|
||||
"autoload": {
|
||||
|
|
@ -4944,9 +4944,19 @@
|
|||
],
|
||||
"support": {
|
||||
"issues": "https://github.com/spatie/temporary-directory/issues",
|
||||
"source": "https://github.com/spatie/temporary-directory/tree/1.3.0"
|
||||
"source": "https://github.com/spatie/temporary-directory/tree/2.0.0"
|
||||
},
|
||||
"time": "2020-11-09T15:54:21+00:00"
|
||||
"funding": [
|
||||
{
|
||||
"url": "https://spatie.be/open-source/support-us",
|
||||
"type": "custom"
|
||||
},
|
||||
{
|
||||
"url": "https://github.com/spatie",
|
||||
"type": "github"
|
||||
}
|
||||
],
|
||||
"time": "2021-03-30T19:46:13+00:00"
|
||||
},
|
||||
{
|
||||
"name": "swiftmailer/swiftmailer",
|
||||
|
|
@ -7552,16 +7562,16 @@
|
|||
"packages-dev": [
|
||||
{
|
||||
"name": "barryvdh/laravel-ide-helper",
|
||||
"version": "v2.9.3",
|
||||
"version": "v2.10.0",
|
||||
"source": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/barryvdh/laravel-ide-helper.git",
|
||||
"reference": "2f61602e7a7f88ad29b0f71355b4bb71396e923b"
|
||||
"reference": "73b1012b927633a1b4cd623c2e6b1678e6faef08"
|
||||
},
|
||||
"dist": {
|
||||
"type": "zip",
|
||||
"url": "https://api.github.com/repos/barryvdh/laravel-ide-helper/zipball/2f61602e7a7f88ad29b0f71355b4bb71396e923b",
|
||||
"reference": "2f61602e7a7f88ad29b0f71355b4bb71396e923b",
|
||||
"url": "https://api.github.com/repos/barryvdh/laravel-ide-helper/zipball/73b1012b927633a1b4cd623c2e6b1678e6faef08",
|
||||
"reference": "73b1012b927633a1b4cd623c2e6b1678e6faef08",
|
||||
"shasum": ""
|
||||
},
|
||||
"require": {
|
||||
|
|
@ -7630,7 +7640,7 @@
|
|||
],
|
||||
"support": {
|
||||
"issues": "https://github.com/barryvdh/laravel-ide-helper/issues",
|
||||
"source": "https://github.com/barryvdh/laravel-ide-helper/tree/v2.9.3"
|
||||
"source": "https://github.com/barryvdh/laravel-ide-helper/tree/v2.10.0"
|
||||
},
|
||||
"funding": [
|
||||
{
|
||||
|
|
@ -7638,7 +7648,7 @@
|
|||
"type": "github"
|
||||
}
|
||||
],
|
||||
"time": "2021-04-02T14:32:13+00:00"
|
||||
"time": "2021-04-09T06:17:55+00:00"
|
||||
},
|
||||
{
|
||||
"name": "barryvdh/reflection-docblock",
|
||||
|
|
@ -8297,16 +8307,16 @@
|
|||
},
|
||||
{
|
||||
"name": "facade/flare-client-php",
|
||||
"version": "1.5.0",
|
||||
"version": "1.6.1",
|
||||
"source": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/facade/flare-client-php.git",
|
||||
"reference": "9dd6f2b56486d939c4467b3f35475d44af57cf17"
|
||||
"reference": "f2b0969f2d9594704be74dbeb25b201570a98098"
|
||||
},
|
||||
"dist": {
|
||||
"type": "zip",
|
||||
"url": "https://api.github.com/repos/facade/flare-client-php/zipball/9dd6f2b56486d939c4467b3f35475d44af57cf17",
|
||||
"reference": "9dd6f2b56486d939c4467b3f35475d44af57cf17",
|
||||
"url": "https://api.github.com/repos/facade/flare-client-php/zipball/f2b0969f2d9594704be74dbeb25b201570a98098",
|
||||
"reference": "f2b0969f2d9594704be74dbeb25b201570a98098",
|
||||
"shasum": ""
|
||||
},
|
||||
"require": {
|
||||
|
|
@ -8350,7 +8360,7 @@
|
|||
],
|
||||
"support": {
|
||||
"issues": "https://github.com/facade/flare-client-php/issues",
|
||||
"source": "https://github.com/facade/flare-client-php/tree/1.5.0"
|
||||
"source": "https://github.com/facade/flare-client-php/tree/1.6.1"
|
||||
},
|
||||
"funding": [
|
||||
{
|
||||
|
|
@ -8358,26 +8368,26 @@
|
|||
"type": "github"
|
||||
}
|
||||
],
|
||||
"time": "2021-03-31T07:32:54+00:00"
|
||||
"time": "2021-04-08T08:50:01+00:00"
|
||||
},
|
||||
{
|
||||
"name": "facade/ignition",
|
||||
"version": "2.7.0",
|
||||
"version": "2.8.3",
|
||||
"source": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/facade/ignition.git",
|
||||
"reference": "bdc8b0b32c888f6edc838ca641358322b3d9506d"
|
||||
"reference": "a8201d51aae83addceaef9344592a3b068b5d64d"
|
||||
},
|
||||
"dist": {
|
||||
"type": "zip",
|
||||
"url": "https://api.github.com/repos/facade/ignition/zipball/bdc8b0b32c888f6edc838ca641358322b3d9506d",
|
||||
"reference": "bdc8b0b32c888f6edc838ca641358322b3d9506d",
|
||||
"url": "https://api.github.com/repos/facade/ignition/zipball/a8201d51aae83addceaef9344592a3b068b5d64d",
|
||||
"reference": "a8201d51aae83addceaef9344592a3b068b5d64d",
|
||||
"shasum": ""
|
||||
},
|
||||
"require": {
|
||||
"ext-json": "*",
|
||||
"ext-mbstring": "*",
|
||||
"facade/flare-client-php": "^1.3.7",
|
||||
"facade/flare-client-php": "^1.6",
|
||||
"facade/ignition-contracts": "^1.0.2",
|
||||
"filp/whoops": "^2.4",
|
||||
"illuminate/support": "^7.0|^8.0",
|
||||
|
|
@ -8435,7 +8445,7 @@
|
|||
"issues": "https://github.com/facade/ignition/issues",
|
||||
"source": "https://github.com/facade/ignition"
|
||||
},
|
||||
"time": "2021-03-30T15:55:38+00:00"
|
||||
"time": "2021-04-09T20:45:59+00:00"
|
||||
},
|
||||
{
|
||||
"name": "facade/ignition-contracts",
|
||||
|
|
@ -8929,16 +8939,16 @@
|
|||
},
|
||||
{
|
||||
"name": "nunomaduro/collision",
|
||||
"version": "v5.3.0",
|
||||
"version": "v5.4.0",
|
||||
"source": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/nunomaduro/collision.git",
|
||||
"reference": "aca63581f380f63a492b1e3114604e411e39133a"
|
||||
"reference": "41b7e9999133d5082700d31a1d0977161df8322a"
|
||||
},
|
||||
"dist": {
|
||||
"type": "zip",
|
||||
"url": "https://api.github.com/repos/nunomaduro/collision/zipball/aca63581f380f63a492b1e3114604e411e39133a",
|
||||
"reference": "aca63581f380f63a492b1e3114604e411e39133a",
|
||||
"url": "https://api.github.com/repos/nunomaduro/collision/zipball/41b7e9999133d5082700d31a1d0977161df8322a",
|
||||
"reference": "41b7e9999133d5082700d31a1d0977161df8322a",
|
||||
"shasum": ""
|
||||
},
|
||||
"require": {
|
||||
|
|
@ -9013,7 +9023,7 @@
|
|||
"type": "patreon"
|
||||
}
|
||||
],
|
||||
"time": "2021-01-25T15:34:13+00:00"
|
||||
"time": "2021-04-09T13:38:32+00:00"
|
||||
},
|
||||
{
|
||||
"name": "nunomaduro/larastan",
|
||||
|
|
@ -9533,16 +9543,16 @@
|
|||
},
|
||||
{
|
||||
"name": "phpstan/phpstan",
|
||||
"version": "0.12.82",
|
||||
"version": "0.12.83",
|
||||
"source": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/phpstan/phpstan.git",
|
||||
"reference": "3920f0fb0aff39263d3a4cb0bca120a67a1a6a11"
|
||||
"reference": "4a967cec6efb46b500dd6d768657336a3ffe699f"
|
||||
},
|
||||
"dist": {
|
||||
"type": "zip",
|
||||
"url": "https://api.github.com/repos/phpstan/phpstan/zipball/3920f0fb0aff39263d3a4cb0bca120a67a1a6a11",
|
||||
"reference": "3920f0fb0aff39263d3a4cb0bca120a67a1a6a11",
|
||||
"url": "https://api.github.com/repos/phpstan/phpstan/zipball/4a967cec6efb46b500dd6d768657336a3ffe699f",
|
||||
"reference": "4a967cec6efb46b500dd6d768657336a3ffe699f",
|
||||
"shasum": ""
|
||||
},
|
||||
"require": {
|
||||
|
|
@ -9573,7 +9583,7 @@
|
|||
"description": "PHPStan - PHP Static Analysis Tool",
|
||||
"support": {
|
||||
"issues": "https://github.com/phpstan/phpstan/issues",
|
||||
"source": "https://github.com/phpstan/phpstan/tree/0.12.82"
|
||||
"source": "https://github.com/phpstan/phpstan/tree/0.12.83"
|
||||
},
|
||||
"funding": [
|
||||
{
|
||||
|
|
@ -9589,7 +9599,7 @@
|
|||
"type": "tidelift"
|
||||
}
|
||||
],
|
||||
"time": "2021-03-19T06:08:17+00:00"
|
||||
"time": "2021-04-03T15:35:45+00:00"
|
||||
},
|
||||
{
|
||||
"name": "phpunit/php-code-coverage",
|
||||
|
|
|
|||
|
|
@ -33,10 +33,11 @@ class IngredientAmountFactory extends Factory
|
|||
$ingredient_unit = 'serving';
|
||||
}
|
||||
|
||||
$amounts = [1/8, 1/4, 1/3, 1/2, 2/3, 3/4, 1, 1 + 1/4, 1 + 1/3, 1 + 1/2, 1 + 2/3, 1 + 3/4, 2, 2 + 1/2, 3];
|
||||
return [
|
||||
'ingredient_id' => $ingredient_factory,
|
||||
'ingredient_type' => $ingredient_type,
|
||||
'amount' => $this->faker->randomElement([1/8, 1/4, 1/2, 3/4, 1, 1.25, 1.5, 1.75, 2, 2.5, 3]),
|
||||
'amount' => $this->faker->randomElement($amounts),
|
||||
'unit' => $ingredient_unit,
|
||||
'detail' => $this->faker->boolean() ? Words::randomWords('a') : null,
|
||||
'weight' => $this->faker->numberBetween(0, 50),
|
||||
|
|
|
|||
|
|
@ -18,7 +18,7 @@ class CreateIngredientAmountsTable extends Migration
|
|||
$table->id();
|
||||
$table->unsignedInteger('ingredient_id');
|
||||
$table->string('ingredient_type');
|
||||
$table->unsignedFloat('amount');
|
||||
$table->decimal('amount', 10, 8)->unsigned();
|
||||
$table->enum('unit', Nutrients::units()->pluck('value')->toArray())->nullable();
|
||||
$table->string('detail')->nullable();
|
||||
$table->unsignedInteger('weight');
|
||||
|
|
|
|||
|
|
@ -1,7 +1,7 @@
|
|||
# For more information: https://laravel.com/docs/sail
|
||||
version: '3'
|
||||
services:
|
||||
kcal.test:
|
||||
app:
|
||||
build:
|
||||
context: ./vendor/laravel/sail/runtimes/8.0
|
||||
dockerfile: Dockerfile
|
||||
|
|
@ -18,10 +18,10 @@ services:
|
|||
networks:
|
||||
- sail
|
||||
depends_on:
|
||||
- mysql
|
||||
- db
|
||||
- redis
|
||||
- elasticsearch
|
||||
mysql:
|
||||
db:
|
||||
image: 'mysql:8.0'
|
||||
ports:
|
||||
- '${DB_PORT:-3306}:3306'
|
||||
|
|
@ -35,6 +35,18 @@ services:
|
|||
- 'mysql-data:/var/lib/mysql'
|
||||
networks:
|
||||
- sail
|
||||
phpmyadmin:
|
||||
image: phpmyadmin
|
||||
restart: always
|
||||
ports:
|
||||
- 8080:80
|
||||
environment:
|
||||
PMA_HOST: db
|
||||
MYSQL_ROOT_PASSWORD: '${DB_PASSWORD:-kcal}'
|
||||
networks:
|
||||
- sail
|
||||
depends_on:
|
||||
- db
|
||||
elasticsearch:
|
||||
image: 'elasticsearch:7.12.0'
|
||||
environment:
|
||||
|
|
|
|||
|
|
@ -21,13 +21,14 @@
|
|||
<server name="APP_ENV" value="testing"/>
|
||||
<server name="BCRYPT_ROUNDS" value="4"/>
|
||||
<server name="CACHE_DRIVER" value="array"/>
|
||||
<server name="DB_CONNECTION" value="sqlite"/>
|
||||
<server name="DB_DATABASE" value=":memory:"/>
|
||||
<server name="ELASTIC_HOST" value="localhost:9200"/>
|
||||
<server name="MAIL_MAILER" value="array"/>
|
||||
<server name="QUEUE_CONNECTION" value="sync"/>
|
||||
<server name="SESSION_DRIVER" value="elastic"/>
|
||||
<server name="SESSION_DRIVER" value="array"/>
|
||||
<server name="TELESCOPE_ENABLED" value="false"/>
|
||||
|
||||
<!-- @todo Figure out how to do MySQL parallel testing inside Sail. -->
|
||||
<server name="DB_CONNECTION" value="sqlite"/>
|
||||
<server name="DB_DATABASE" value=":memory:"/>
|
||||
</php>
|
||||
</phpunit>
|
||||
|
|
|
|||
File diff suppressed because one or more lines are too long
|
|
@ -0,0 +1,3 @@
|
|||
<a {{ $attributes->merge(['class' => "px-4 py-2 bg-gray-800 border border-transparent rounded-md font-semibold text-xs text-white text-center uppercase tracking-widest hover:bg-gray-700 active:bg-gray-900 focus:outline-none focus:border-gray-900 focus:ring ring-gray-300 disabled:opacity-25 transition ease-in-out duration-150"]) }}>
|
||||
{{ $slot }}
|
||||
</a>
|
||||
|
|
@ -0,0 +1,3 @@
|
|||
<x-button-link.base :attributes="$attributes" class="bg-green-800 hover:bg-green-700 active:bg-green-900 focus:border-green-900 ring-green-300">
|
||||
{{ $slot }}
|
||||
</x-button-link.base>
|
||||
|
|
@ -0,0 +1,3 @@
|
|||
<x-button-link.base :attributes="$attributes" class="bg-red-800 hover:bg-red-700 active:bg-red-900 focus:border-red-900 ring-red-300">
|
||||
{{ $slot }}
|
||||
</x-button-link.base>
|
||||
|
|
@ -3,12 +3,9 @@
|
|||
<x-slot name="header">
|
||||
<div class="flex justify-between items-center">
|
||||
<h1 class="font-semibold text-2xl text-gray-800 leading-tight">Foods</h1>
|
||||
<a href="{{ route('foods.create') }}" class="inline-flex items-center rounded-md font-semibold text-white p-2 bg-green-500 tracking-widest hover:bg-green-700 active:bg-green-900 focus:outline-none focus:border-green-900 focus:ring ring-green-600 disabled:opacity-25 transition ease-in-out duration-150">
|
||||
<svg class="w-6 h-6" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 20 20" fill="currentColor">
|
||||
<path fill-rule="evenodd" d="M10 5a1 1 0 011 1v3h3a1 1 0 110 2h-3v3a1 1 0 11-2 0v-3H6a1 1 0 110-2h3V6a1 1 0 011-1z" clip-rule="evenodd" />
|
||||
</svg>
|
||||
New Food
|
||||
</a>
|
||||
<x-button-link.green href="{{ route('foods.create') }}" class="text-sm">
|
||||
Add Food
|
||||
</x-button-link.green>
|
||||
</div>
|
||||
</x-slot>
|
||||
<x-search-view :route="route('api:v1:foods.index')" :tags="$tags">
|
||||
|
|
|
|||
|
|
@ -4,82 +4,46 @@
|
|||
<h1 class="font-semibold text-xl text-gray-800 leading-tight flex flex-auto items-center">
|
||||
<div>
|
||||
{{ $food->name }}@if($food->detail), {{ $food->detail }}@endif
|
||||
@if($food->brand)
|
||||
<div>{{ $food->brand }}</div>
|
||||
@endif
|
||||
</div>
|
||||
<a class="ml-2 text-gray-500 hover:text-gray-700 hover:border-gray-300 text-sm"
|
||||
href="{{ route('foods.edit', $food) }}">
|
||||
<svg class="h-6 w-6" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 20 20" fill="currentColor">
|
||||
<path d="M17.414 2.586a2 2 0 00-2.828 0L7 10.172V13h2.828l7.586-7.586a2 2 0 000-2.828z" />
|
||||
<path fill-rule="evenodd" d="M2 6a2 2 0 012-2h4a1 1 0 010 2H4v10h10v-4a1 1 0 112 0v4a2 2 0 01-2 2H4a2 2 0 01-2-2V6z" clip-rule="evenodd" />
|
||||
</svg>
|
||||
</a>
|
||||
<a class="h-6 w-6 text-red-500 hover:text-red-700 hover:border-red-300 float-right text-sm"
|
||||
href="{{ route('foods.delete', $food) }}">
|
||||
<svg 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>
|
||||
</a>
|
||||
</h1>
|
||||
</x-slot>
|
||||
<article class="flex flex-col space-y-2 sm:flex-row sm:space-x-4 sm:space-y-0">
|
||||
<section class="p-1 mb-2 border-2 border-black font-sans md:w-72">
|
||||
<h1 class="text-3xl font-extrabold leading-none">Nutrition Facts</h1>
|
||||
<section class="flex justify-between font-bold border-b-8 border-black">
|
||||
<h1>Serving size</h1>
|
||||
<div>
|
||||
{{ $food->servingSizeFormatted }}
|
||||
{{ $food->servingUnitFormatted ?? $food->name }}
|
||||
({{ $food->serving_weight }}g)
|
||||
</div>
|
||||
</section>
|
||||
<h2 class="font-bold text-right">Amount per serving</h2>
|
||||
<section class="flex justify-between items-end font-extrabold">
|
||||
<h1 class="text-3xl">Calories</h1>
|
||||
<div class="text-4xl">{{ $food->calories }}</div>
|
||||
</section>
|
||||
<div class="border-t-4 border-black text-sm">
|
||||
<hr class="border-gray-500"/>
|
||||
<section class="flex justify-between">
|
||||
<h1 class="font-bold">Total Fat</h1>
|
||||
<div>{{ $food->fat }}g</div>
|
||||
</section>
|
||||
<hr class="border-gray-500"/>
|
||||
<section class="flex justify-between">
|
||||
<h1 class="font-bold">Cholesterol</h1>
|
||||
<div>{{ $food->cholesterol }}mg</div>
|
||||
</section>
|
||||
<hr class="border-gray-500"/>
|
||||
<section class="flex justify-between">
|
||||
<h1 class="font-bold">Sodium</h1>
|
||||
<div>{{ $food->sodium }}mg</div>
|
||||
</section>
|
||||
<hr class="border-gray-500"/>
|
||||
<section class="flex justify-between">
|
||||
<h1 class="font-bold">Total Carbohydrate</h1>
|
||||
<div>{{ $food->carbohydrates }}g</div>
|
||||
</section>
|
||||
<hr class="border-gray-500"/>
|
||||
<section class="flex justify-between">
|
||||
<h1 class="font-bold">Protein</h1>
|
||||
<div>{{ $food->protein }}g</div>
|
||||
</section>
|
||||
</div>
|
||||
</section>
|
||||
<section class="flex flex-col space-y-2">
|
||||
@if(!$food->tags->isEmpty())
|
||||
<h1 class="font-bold text-2xl">Tags</h1>
|
||||
<div class="flex flex-wrap">
|
||||
@foreach ($food->tags as $tag)
|
||||
<span class="m-1 bg-gray-200 rounded-full px-2 leading-loose">{{ $tag->name }}</span>
|
||||
@endforeach
|
||||
</div>
|
||||
@endif
|
||||
@if($food->description)
|
||||
<h1 class="font-bold text-2xl">Description</h1>
|
||||
<p class="text-gray-800">{{ $food->description }}</p>
|
||||
@endif
|
||||
<div class="flex flex-col justify-between pb-4 md:flex-row md:space-x-4">
|
||||
<div class="flex-1">
|
||||
<section class="flex flex-col space-y-2">
|
||||
@if($food->brand)
|
||||
<h1 class="font-bold text-2xl">Brand</h1>
|
||||
<div class="flex flex-wrap">
|
||||
{{ $food->brand }}
|
||||
</div>
|
||||
@endif
|
||||
@if($food->notes)
|
||||
<h1 class="font-bold text-2xl">Notes</h1>
|
||||
<div class="flex flex-wrap">
|
||||
{{ $food->notes }}
|
||||
</div>
|
||||
@endif
|
||||
@if(!$food->tags->isEmpty())
|
||||
<h1 class="font-bold text-2xl">Tags</h1>
|
||||
<div class="flex flex-wrap">
|
||||
@foreach ($food->tags as $tag)
|
||||
<span class="m-1 bg-gray-200 rounded-full px-2 leading-loose">{{ $tag->name }}</span>
|
||||
@endforeach
|
||||
</div>
|
||||
@endif
|
||||
@if($food->description)
|
||||
<h1 class="font-bold text-2xl">Description</h1>
|
||||
<p class="text-gray-800">{{ $food->description }}</p>
|
||||
@endif
|
||||
@if($food->source)
|
||||
<h1 class="font-bold text-2xl">Source</h1>
|
||||
<p>
|
||||
@if(filter_var($food->source, FILTER_VALIDATE_URL))
|
||||
<a class="text-gray-500 hover:text-gray-700" href="{{ $food->source }}">{{ $food->source }}</a>
|
||||
@else
|
||||
{{ $food->source }}
|
||||
@endif
|
||||
</p>
|
||||
@endif
|
||||
@if(!$food->ingredientAmountRelationships->isEmpty())
|
||||
<h1 class="font-bold text-2xl">Recipes</h1>
|
||||
<ul class="list-disc list-inside ml-3 space-y-1">
|
||||
|
|
@ -89,16 +53,61 @@
|
|||
@endforeach
|
||||
</ul>
|
||||
@endif
|
||||
@if($food->source)
|
||||
<h1 class="font-bold text-2xl">Source</h1>
|
||||
<p>
|
||||
@if(filter_var($food->source, FILTER_VALIDATE_URL))
|
||||
<a class="text-gray-500 hover:text-gray-700" href="{{ $food->source }}">{{ $food->source }}</a>
|
||||
@else
|
||||
{{ $food->source }}
|
||||
@endif
|
||||
</p>
|
||||
@endif
|
||||
</section>
|
||||
</article>
|
||||
</section>
|
||||
</div>
|
||||
<aside class="flex flex-col space-y-4 mt-8 sm:mt-0">
|
||||
<section class="p-1 mb-2 border-2 border-black font-sans md:w-72">
|
||||
<h1 class="text-3xl font-extrabold leading-none">Nutrition Facts</h1>
|
||||
<section class="flex justify-between font-bold border-b-8 border-black">
|
||||
<h1>Serving size</h1>
|
||||
<div>
|
||||
{{ $food->servingSizeFormatted }}
|
||||
{{ $food->servingUnitFormatted ?? $food->name }}
|
||||
({{ $food->serving_weight }}g)
|
||||
</div>
|
||||
</section>
|
||||
<h2 class="font-bold text-right">Amount per serving</h2>
|
||||
<section class="flex justify-between items-end font-extrabold">
|
||||
<h1 class="text-3xl">Calories</h1>
|
||||
<div class="text-4xl">{{ $food->calories }}</div>
|
||||
</section>
|
||||
<div class="border-t-4 border-black text-sm">
|
||||
<hr class="border-gray-500"/>
|
||||
<section class="flex justify-between">
|
||||
<h1 class="font-bold">Total Fat</h1>
|
||||
<div>{{ $food->fat }}g</div>
|
||||
</section>
|
||||
<hr class="border-gray-500"/>
|
||||
<section class="flex justify-between">
|
||||
<h1 class="font-bold">Cholesterol</h1>
|
||||
<div>{{ $food->cholesterol }}mg</div>
|
||||
</section>
|
||||
<hr class="border-gray-500"/>
|
||||
<section class="flex justify-between">
|
||||
<h1 class="font-bold">Sodium</h1>
|
||||
<div>{{ $food->sodium }}mg</div>
|
||||
</section>
|
||||
<hr class="border-gray-500"/>
|
||||
<section class="flex justify-between">
|
||||
<h1 class="font-bold">Total Carbohydrate</h1>
|
||||
<div>{{ $food->carbohydrates }}g</div>
|
||||
</section>
|
||||
<hr class="border-gray-500"/>
|
||||
<section class="flex justify-between">
|
||||
<h1 class="font-bold">Protein</h1>
|
||||
<div>{{ $food->protein }}g</div>
|
||||
</section>
|
||||
</div>
|
||||
</section>
|
||||
<hr />
|
||||
<section class="flex flex-col space-y-2">
|
||||
<x-button-link.base href="{{ route('foods.edit', $food) }}">
|
||||
Edit Food
|
||||
</x-button-link.base>
|
||||
<x-button-link.red href="{{ route('foods.delete', $food) }}">
|
||||
Delete Food
|
||||
</x-button-link.red>
|
||||
</section>
|
||||
</aside>
|
||||
</div>
|
||||
</x-app-layout>
|
||||
|
|
|
|||
|
|
@ -13,7 +13,16 @@
|
|||
</svg>
|
||||
</a>
|
||||
</div>
|
||||
<div class="text-base text-gray-500">{{ $date->format('D, j M Y') }}</div>
|
||||
<div class="text-base text-gray-500">
|
||||
<form x-data method="GET" action="{{ route('goals.index') }}">
|
||||
<x-inputs.input name="date"
|
||||
type="date"
|
||||
class="border-0 shadow-none p-0 text-center"
|
||||
:value="$date->toDateString()"
|
||||
x-on:change="$el.submit();"
|
||||
required />
|
||||
</form>
|
||||
</div>
|
||||
<div>
|
||||
<a class="text-gray-500 hover:text-gray-700 hover:border-gray-300"
|
||||
href="{{ route(Route::current()->getName(), ['date' => $date->copy()->addDay(1)->toDateString()]) }}">
|
||||
|
|
@ -24,12 +33,9 @@
|
|||
</div>
|
||||
</div>
|
||||
</h1>
|
||||
<a href="{{ route('goals.create') }}" class="inline-flex items-center rounded-md font-semibold text-white p-2 bg-green-500 tracking-widest hover:bg-green-700 active:bg-green-900 focus:outline-none focus:border-green-900 focus:ring ring-green-600 disabled:opacity-25 transition ease-in-out duration-150">
|
||||
<svg class="w-6 h-6" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 20 20" fill="currentColor">
|
||||
<path fill-rule="evenodd" d="M10 5a1 1 0 011 1v3h3a1 1 0 110 2h-3v3a1 1 0 11-2 0v-3H6a1 1 0 110-2h3V6a1 1 0 011-1z" clip-rule="evenodd" />
|
||||
</svg>
|
||||
<x-button-link.green href="{{ route('goals.create') }}" class="text-sm">
|
||||
Add Goal
|
||||
</a>
|
||||
</x-button-link.green>
|
||||
</div>
|
||||
</x-slot>
|
||||
<div class="space-y-4">
|
||||
|
|
|
|||
|
|
@ -3,9 +3,9 @@
|
|||
<x-slot name="header">
|
||||
<div class="flex justify-between items-center">
|
||||
<h1 class="font-semibold text-xl text-gray-800 leading-tight">Add Entry</h1>
|
||||
<a href="{{ route('journal-entries.create', ['date' => $default_date->format('Y-m-d')]) }}" class="inline-flex items-center rounded-md font-semibold text-white p-2 bg-green-500 tracking-widest hover:bg-green-700 active:bg-green-900 focus:outline-none focus:border-green-900 focus:ring ring-green-600 disabled:opacity-25 transition ease-in-out duration-150">
|
||||
Add by Recipes/Food
|
||||
</a>
|
||||
<x-button-link.green href="{{ route('journal-entries.create', ['date' => $default_date->format('Y-m-d')]) }}" class="text-sm">
|
||||
Add by Recipes/Foods
|
||||
</x-button-link.green>
|
||||
</div>
|
||||
</x-slot>
|
||||
<form method="POST" action="{{ route('journal-entries.store.from-nutrients') }}">
|
||||
|
|
|
|||
|
|
@ -3,9 +3,9 @@
|
|||
<x-slot name="header">
|
||||
<div class="flex justify-between items-center">
|
||||
<h1 class="font-semibold text-xl text-gray-800 leading-tight">Add Entries</h1>
|
||||
<a href="{{ route('journal-entries.create.from-nutrients', ['date' => $default_date->format('Y-m-d')]) }}" class="inline-flex items-center rounded-md font-semibold text-white p-2 bg-green-500 tracking-widest hover:bg-green-700 active:bg-green-900 focus:outline-none focus:border-green-900 focus:ring ring-green-600 disabled:opacity-25 transition ease-in-out duration-150">
|
||||
<x-button-link.green href="{{ route('journal-entries.create.from-nutrients', ['date' => $default_date->format('Y-m-d')]) }}" class="text-sm">
|
||||
Add by Nutrients
|
||||
</a>
|
||||
</x-button-link.green>
|
||||
</div>
|
||||
</x-slot>
|
||||
<form method="POST" action="{{ route('journal-entries.store') }}">
|
||||
|
|
@ -17,14 +17,14 @@
|
|||
<div class="journal-entry-template hidden">
|
||||
@include('journal-entries.partials.entry-item-input', ['default_date' => $default_date])
|
||||
</div>
|
||||
<x-inputs.icon-green class="add-entry-item" x-on:click="addEntryNode($el);">
|
||||
<x-inputs.icon-green type="button" class="add-entry-item" x-on:click="addEntryNode($el);">
|
||||
<svg class="h-10 w-10 pointer-events-none" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 20 20" fill="currentColor">
|
||||
<path fill-rule="evenodd" d="M10 18a8 8 0 100-16 8 8 0 000 16zm1-11a1 1 0 10-2 0v2H7a1 1 0 100 2h2v2a1 1 0 102 0v-2h2a1 1 0 100-2h-2V7z" clip-rule="evenodd" />
|
||||
</svg>
|
||||
</x-inputs.icon-green>
|
||||
<div class="flex items-center justify-end mt-4 space-x-4">
|
||||
<fieldset class="flex space-x-2">
|
||||
<x-inputs.input name="group_entries" type="checkbox" class="h-5 w-5" value="1" checked />
|
||||
<x-inputs.input name="group_entries" type="checkbox" class="h-5 w-5" value="1" />
|
||||
<x-inputs.label for="groupEntries" value="Group entries by day and meal" />
|
||||
</fieldset>
|
||||
<x-inputs.button x-on:click="removeTemplate($el);">Add entries</x-inputs.button>
|
||||
|
|
|
|||
|
|
@ -13,7 +13,16 @@
|
|||
</svg>
|
||||
</a>
|
||||
</div>
|
||||
<div class="text-base text-gray-500">{{ $date->format('D, j M Y') }}</div>
|
||||
<div class="text-base text-gray-500">
|
||||
<form x-data method="GET" action="{{ route('journal-entries.index') }}">
|
||||
<x-inputs.input name="date"
|
||||
type="date"
|
||||
class="border-0 shadow-none p-0 text-center"
|
||||
:value="$date->toDateString()"
|
||||
x-on:change="$el.submit();"
|
||||
required />
|
||||
</form>
|
||||
</div>
|
||||
<div>
|
||||
<a class="text-gray-500 hover:text-gray-700 hover:border-gray-300"
|
||||
href="{{ route(Route::current()->getName(), ['date' => $date->copy()->addDay(1)->toDateString()]) }}">
|
||||
|
|
@ -24,12 +33,9 @@
|
|||
</div>
|
||||
</div>
|
||||
</h1>
|
||||
<a href="{{ route('journal-entries.create', ['date' => $date->format('Y-m-d')]) }}" class="inline-flex items-center rounded-md font-semibold text-white p-2 bg-green-500 tracking-widest hover:bg-green-700 active:bg-green-900 focus:outline-none focus:border-green-900 focus:ring ring-green-600 disabled:opacity-25 transition ease-in-out duration-150">
|
||||
<svg class="w-6 h-6" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 20 20" fill="currentColor">
|
||||
<path fill-rule="evenodd" d="M10 5a1 1 0 011 1v3h3a1 1 0 110 2h-3v3a1 1 0 11-2 0v-3H6a1 1 0 110-2h3V6a1 1 0 011-1z" clip-rule="evenodd" />
|
||||
</svg>
|
||||
New Entry
|
||||
</a>
|
||||
<x-button-link.green href="{{ route('journal-entries.create', ['date' => $date->format('Y-m-d')]) }}" class="text-sm">
|
||||
Add Entry
|
||||
</x-button-link.green>
|
||||
</div>
|
||||
</x-slot>
|
||||
<div class="flex align-top flex-col space-y-4 sm:flex-row sm:space-x-4 sm:space-y-0">
|
||||
|
|
@ -104,7 +110,7 @@
|
|||
</div>
|
||||
<span class="text-sm text-gray-500">
|
||||
@foreach(\App\Support\Nutrients::all()->sortBy('weight') as $nutrient)
|
||||
{{ round($entries->where('meal', $meal)->sum($nutrient['value']), 2) }}{{ $nutrient['unit'] }}
|
||||
{{ \App\Support\Nutrients::round($entries->where('meal', $meal)->sum($nutrient['value']), $nutrient['value']) }}{{ $nutrient['unit'] }}
|
||||
{{ $nutrient['value'] }}@if(!$loop->last), @endif
|
||||
@endforeach
|
||||
</span>
|
||||
|
|
@ -124,7 +130,7 @@
|
|||
<div>
|
||||
<span class="font-bold">nutrients:</span>
|
||||
@foreach(\App\Support\Nutrients::all()->sortBy('weight') as $nutrient)
|
||||
{{ round($entry->{$nutrient['value']}, 2) }}{{ $nutrient['unit'] }}
|
||||
{{ \App\Support\Nutrients::round($entry->{$nutrient['value']}, $nutrient['value']) }}{{ $nutrient['unit'] }}
|
||||
{{ $nutrient['value'] }}@if(!$loop->last), @endif
|
||||
@endforeach
|
||||
</div>
|
||||
|
|
|
|||
|
|
@ -38,7 +38,8 @@
|
|||
size="5"
|
||||
class="block w-full"
|
||||
placeholder="Amount"
|
||||
:value="$amount ?? null" />
|
||||
:value="$amount ?? null"
|
||||
required />
|
||||
</div>
|
||||
|
||||
<!-- Unit -->
|
||||
|
|
|
|||
|
|
@ -3,12 +3,9 @@
|
|||
<x-slot name="header">
|
||||
<div class="flex justify-between items-center">
|
||||
<h1 class="font-semibold text-2xl text-gray-800 leading-tight">Recipes</h1>
|
||||
<a href="{{ route('recipes.create') }}" class="inline-flex items-center rounded-md font-semibold text-white p-2 bg-green-500 tracking-widest hover:bg-green-700 active:bg-green-900 focus:outline-none focus:border-green-900 focus:ring ring-green-600 disabled:opacity-25 transition ease-in-out duration-150">
|
||||
<svg class="w-6 h-6" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 20 20" fill="currentColor">
|
||||
<path fill-rule="evenodd" d="M10 5a1 1 0 011 1v3h3a1 1 0 110 2h-3v3a1 1 0 11-2 0v-3H6a1 1 0 110-2h3V6a1 1 0 011-1z" clip-rule="evenodd" />
|
||||
</svg>
|
||||
New Recipe
|
||||
</a>
|
||||
<x-button-link.green href="{{ route('recipes.create') }}" class="text-sm">
|
||||
Add Recipe
|
||||
</x-button-link.green>
|
||||
</div>
|
||||
</x-slot>
|
||||
<x-search-view :route="route('api:v1:recipes.index')" :tags="$tags">
|
||||
|
|
|
|||
|
|
@ -6,25 +6,12 @@
|
|||
<x-slot name="header">
|
||||
<h1 class="font-semibold text-xl text-gray-800 leading-tight flex flex-auto">
|
||||
{{ $recipe->name }}
|
||||
<a class="ml-2 text-gray-500 hover:text-gray-700 hover:border-gray-300 text-sm"
|
||||
href="{{ route('recipes.edit', $recipe) }}">
|
||||
<svg class="h-6 w-6" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 20 20" fill="currentColor">
|
||||
<path d="M17.414 2.586a2 2 0 00-2.828 0L7 10.172V13h2.828l7.586-7.586a2 2 0 000-2.828z" />
|
||||
<path fill-rule="evenodd" d="M2 6a2 2 0 012-2h4a1 1 0 010 2H4v10h10v-4a1 1 0 112 0v4a2 2 0 01-2 2H4a2 2 0 01-2-2V6z" clip-rule="evenodd" />
|
||||
</svg>
|
||||
</a>
|
||||
<a class="h-6 w-6 text-red-500 hover:text-red-700 hover:border-red-300 float-right text-sm"
|
||||
href="{{ route('recipes.delete', $recipe) }}">
|
||||
<svg 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>
|
||||
</a>
|
||||
</h1>
|
||||
</x-slot>
|
||||
<div class="flex flex-col justify-between pb-4 md:flex-row md:space-x-4">
|
||||
<div class="flex-1" x-data="{showNutrientsSummary: false}">
|
||||
@if($recipe->time_total > 0)
|
||||
<section class="flex justify-between mb-2 p-2 bg-gray-100 rounded">
|
||||
<section class="flex justify-between mb-2 p-2 bg-gray-100 rounded max-w-3xl">
|
||||
<div>
|
||||
<h1 class="mb-1 font-bold">Prep time</h1>
|
||||
<p class="text-gray-800 text-sm">{{ $recipe->time_prep }} minutes</p>
|
||||
|
|
@ -56,7 +43,7 @@
|
|||
@if($item::class === \App\Models\IngredientAmount::class)
|
||||
<li>
|
||||
<span>
|
||||
{{ \App\Support\Number::fractionStringFromFloat($item->amount) }}
|
||||
{{ \App\Support\Number::rationalStringFromFloat($item->amount) }}
|
||||
@if($item->unitFormatted){{ $item->unitFormatted }}@endif
|
||||
@if($item->ingredient->type === \App\Models\Recipe::class)
|
||||
<a class="text-gray-500 hover:text-gray-700 hover:border-gray-300"
|
||||
|
|
@ -94,8 +81,20 @@
|
|||
</ol>
|
||||
</div>
|
||||
</section>
|
||||
<footer>
|
||||
@if(!$recipe->tags->isEmpty())
|
||||
<section>
|
||||
<h1 class="mb-2 font-bold text-2xl">Tags</h1>
|
||||
<div class="flex flex-wrap">
|
||||
@foreach($recipe->tags as $tag)
|
||||
<span class="m-1 bg-gray-200 rounded-full px-2 leading-loose cursor-default">{{ $tag->name }}</span>
|
||||
@endforeach
|
||||
</div>
|
||||
</section>
|
||||
@endif
|
||||
</footer>
|
||||
</div>
|
||||
<aside class="flex flex-col space-y-4 md:w-1/2 lg:w-1/3">
|
||||
<aside class="flex flex-col space-y-4 mt-8 sm:mt-0">
|
||||
<div class="p-1 border-2 border-black font-sans w-72">
|
||||
<div class="text-3xl font-extrabold leading-none">Nutrition Facts</div>
|
||||
<div class="leading-snug">{{ $recipe->servings }} {{ \Illuminate\Support\Str::plural('serving', $recipe->servings ) }}</div>
|
||||
|
|
@ -149,16 +148,15 @@
|
|||
@endif
|
||||
</section>
|
||||
@endif
|
||||
@if(!$recipe->tags->isEmpty())
|
||||
<section>
|
||||
<h1 class="mb-2 font-bold text-2xl">Tags</h1>
|
||||
<div class="flex flex-wrap">
|
||||
@foreach($recipe->tags as $tag)
|
||||
<span class="m-1 bg-gray-200 rounded-full px-2 leading-loose cursor-default">{{ $tag->name }}</span>
|
||||
@endforeach
|
||||
</div>
|
||||
</section>
|
||||
@endif
|
||||
<hr />
|
||||
<section class="flex flex-col space-y-2">
|
||||
<x-button-link.base href="{{ route('recipes.edit', $recipe) }}">
|
||||
Edit Recipe
|
||||
</x-button-link.base>
|
||||
<x-button-link.red href="{{ route('recipes.delete', $recipe) }}">
|
||||
Delete Recipe
|
||||
</x-button-link.red>
|
||||
</section>
|
||||
</aside>
|
||||
</div>
|
||||
</x-app-layout>
|
||||
|
|
|
|||
|
|
@ -3,12 +3,10 @@
|
|||
namespace Tests\Feature\Console;
|
||||
|
||||
use App\Models\User;
|
||||
use Illuminate\Foundation\Testing\RefreshDatabase;
|
||||
use Tests\TestCase;
|
||||
|
||||
class UserAddTest extends TestCase
|
||||
{
|
||||
use RefreshDatabase;
|
||||
|
||||
public function testCanAddUserInteractively(): void
|
||||
{
|
||||
|
|
|
|||
|
|
@ -5,14 +5,11 @@ namespace Tests\Feature\Http\Controllers\Auth;
|
|||
use App\Models\User;
|
||||
use App\Providers\RouteServiceProvider;
|
||||
use Illuminate\Auth\Events\Lockout;
|
||||
use Illuminate\Foundation\Testing\RefreshDatabase;
|
||||
use Illuminate\Support\Facades\Event;
|
||||
use Illuminate\Validation\ValidationException;
|
||||
use Tests\TestCase;
|
||||
|
||||
class AuthenticationTest extends TestCase
|
||||
{
|
||||
use RefreshDatabase;
|
||||
|
||||
public function testLoginScreenCanRendered(): void
|
||||
{
|
||||
|
|
|
|||
|
|
@ -5,11 +5,9 @@ namespace Tests\Feature\Http\Controllers;
|
|||
use App\Http\Controllers\FoodController;
|
||||
use App\Models\Food;
|
||||
use Database\Factories\FoodFactory;
|
||||
use Illuminate\Foundation\Testing\RefreshDatabase;
|
||||
|
||||
class FoodControllerTest extends HttpControllerTestCase
|
||||
{
|
||||
use RefreshDatabase;
|
||||
|
||||
/**
|
||||
* @inheritdoc
|
||||
|
|
|
|||
|
|
@ -5,11 +5,9 @@ namespace Tests\Feature\Http\Controllers;
|
|||
use App\Http\Controllers\GoalController;
|
||||
use App\Models\Goal;
|
||||
use Database\Factories\GoalFactory;
|
||||
use Illuminate\Foundation\Testing\RefreshDatabase;
|
||||
|
||||
class GoalControllerTest extends HttpControllerTestCase
|
||||
{
|
||||
use RefreshDatabase;
|
||||
|
||||
/**
|
||||
* @inheritdoc
|
||||
|
|
|
|||
|
|
@ -6,7 +6,6 @@ use App\Http\Controllers\IngredientPickerController;
|
|||
use App\Models\Food;
|
||||
use App\Models\Recipe;
|
||||
use GuzzleHttp\Exception\ConnectException;
|
||||
use Illuminate\Foundation\Testing\RefreshDatabase;
|
||||
use Illuminate\Support\Facades\Config;
|
||||
use Tests\LoggedInTestCase;
|
||||
|
||||
|
|
@ -15,7 +14,6 @@ use Tests\LoggedInTestCase;
|
|||
*/
|
||||
class IngredientPickerControllerTest extends LoggedInTestCase
|
||||
{
|
||||
use RefreshDatabase;
|
||||
|
||||
private function buildUrl(array $parameters = []): string
|
||||
{
|
||||
|
|
|
|||
|
|
@ -6,12 +6,11 @@ use App\Http\Controllers\JournalEntryController;
|
|||
use App\Models\IngredientAmount;
|
||||
use App\Models\JournalEntry;
|
||||
use Database\Factories\JournalEntryFactory;
|
||||
use Illuminate\Foundation\Testing\RefreshDatabase;
|
||||
use Illuminate\Foundation\Testing\WithFaker;
|
||||
|
||||
class JournalEntryControllerTest extends HttpControllerTestCase
|
||||
{
|
||||
use RefreshDatabase, WithFaker;
|
||||
use WithFaker;
|
||||
|
||||
/**
|
||||
* @inheritdoc
|
||||
|
|
|
|||
|
|
@ -9,14 +9,12 @@ use App\Models\RecipeSeparator;
|
|||
use App\Models\RecipeStep;
|
||||
use Database\Factories\RecipeFactory;
|
||||
use Illuminate\Database\Eloquent\Collection;
|
||||
use Illuminate\Foundation\Testing\RefreshDatabase;
|
||||
use Illuminate\Foundation\Testing\WithFaker;
|
||||
use Illuminate\Http\UploadedFile;
|
||||
use Illuminate\Support\Facades\Storage;
|
||||
|
||||
class RecipeControllerTest extends HttpControllerTestCase
|
||||
{
|
||||
use RefreshDatabase, WithFaker;
|
||||
use WithFaker;
|
||||
|
||||
/**
|
||||
* @inheritdoc
|
||||
|
|
|
|||
|
|
@ -3,15 +3,13 @@
|
|||
namespace Tests\Feature\JsonApi;
|
||||
|
||||
use App\Models\Food;
|
||||
use App\Models\Recipe;
|
||||
use Database\Factories\FoodFactory;
|
||||
use Illuminate\Database\Eloquent\Collection;
|
||||
use Illuminate\Foundation\Testing\RefreshDatabase;
|
||||
|
||||
use Tests\Feature\JsonApi\Traits\HasTags;
|
||||
|
||||
class FoodApiTest extends JsonApiTestCase
|
||||
{
|
||||
use RefreshDatabase, HasTags;
|
||||
use HasTags;
|
||||
|
||||
/**
|
||||
* @inheritdoc
|
||||
|
|
|
|||
|
|
@ -5,12 +5,12 @@ namespace Tests\Feature\JsonApi;
|
|||
use App\Models\Goal;
|
||||
use Database\Factories\GoalFactory;
|
||||
use Illuminate\Database\Eloquent\Collection;
|
||||
use Illuminate\Foundation\Testing\RefreshDatabase;
|
||||
|
||||
use Tests\Feature\JsonApi\Traits\BelongsToUser;
|
||||
|
||||
class GoalApiTest extends JsonApiTestCase
|
||||
{
|
||||
use RefreshDatabase, BelongsToUser;
|
||||
use BelongsToUser;
|
||||
|
||||
/**
|
||||
* @inheritdoc
|
||||
|
|
|
|||
|
|
@ -7,11 +7,9 @@ use App\Models\IngredientAmount;
|
|||
use App\Models\JournalEntry;
|
||||
use App\Models\Recipe;
|
||||
use Database\Factories\IngredientAmountFactory;
|
||||
use Illuminate\Foundation\Testing\RefreshDatabase;
|
||||
|
||||
class IngredientAmountApiTest extends JsonApiTestCase
|
||||
{
|
||||
use RefreshDatabase;
|
||||
|
||||
/**
|
||||
* @inheritdoc
|
||||
|
|
|
|||
|
|
@ -5,12 +5,11 @@ namespace Tests\Feature\JsonApi;
|
|||
use App\Models\JournalEntry;
|
||||
use Database\Factories\JournalEntryFactory;
|
||||
use Illuminate\Database\Eloquent\Collection;
|
||||
use Illuminate\Foundation\Testing\RefreshDatabase;
|
||||
use Tests\Feature\JsonApi\Traits\BelongsToUser;
|
||||
|
||||
class JournalEntryApiTest extends JsonApiTestCase
|
||||
{
|
||||
use RefreshDatabase, BelongsToUser;
|
||||
use BelongsToUser;
|
||||
|
||||
/**
|
||||
* @inheritdoc
|
||||
|
|
|
|||
|
|
@ -5,11 +5,9 @@ namespace Tests\Feature\JsonApi;
|
|||
use App\Models\Recipe;
|
||||
use Illuminate\Database\Eloquent\Collection;
|
||||
use Illuminate\Database\Eloquent\Factories\Factory;
|
||||
use Illuminate\Foundation\Testing\RefreshDatabase;
|
||||
|
||||
class MediumApiTest extends JsonApiTestCase
|
||||
{
|
||||
use RefreshDatabase;
|
||||
|
||||
/**
|
||||
* @inheritdoc
|
||||
|
|
|
|||
|
|
@ -4,12 +4,11 @@ namespace Tests\Feature\JsonApi;
|
|||
|
||||
use App\Models\Recipe;
|
||||
use Database\Factories\RecipeFactory;
|
||||
use Illuminate\Foundation\Testing\RefreshDatabase;
|
||||
use Tests\Feature\JsonApi\Traits\HasTags;
|
||||
|
||||
class RecipeApiTest extends JsonApiTestCase
|
||||
{
|
||||
use RefreshDatabase, HasTags;
|
||||
use HasTags;
|
||||
|
||||
/**
|
||||
* @inheritdoc
|
||||
|
|
|
|||
|
|
@ -6,12 +6,11 @@ use App\Models\Recipe;
|
|||
use App\Models\RecipeSeparator;
|
||||
use Database\Factories\RecipeSeparatorFactory;
|
||||
use Illuminate\Database\Eloquent\Collection;
|
||||
use Illuminate\Foundation\Testing\RefreshDatabase;
|
||||
use Tests\Feature\JsonApi\Traits\BelongsToRecipe;
|
||||
|
||||
class RecipeSeparatorApiTest extends JsonApiTestCase
|
||||
{
|
||||
use RefreshDatabase, BelongsToRecipe;
|
||||
use BelongsToRecipe;
|
||||
|
||||
/**
|
||||
* @inheritdoc
|
||||
|
|
|
|||
|
|
@ -6,12 +6,11 @@ use App\Models\Recipe;
|
|||
use App\Models\RecipeStep;
|
||||
use Database\Factories\RecipeStepFactory;
|
||||
use Illuminate\Database\Eloquent\Collection;
|
||||
use Illuminate\Foundation\Testing\RefreshDatabase;
|
||||
use Tests\Feature\JsonApi\Traits\BelongsToRecipe;
|
||||
|
||||
class RecipeStepApiTest extends JsonApiTestCase
|
||||
{
|
||||
use RefreshDatabase, BelongsToRecipe;
|
||||
use BelongsToRecipe;
|
||||
|
||||
/**
|
||||
* @inheritdoc
|
||||
|
|
|
|||
|
|
@ -4,11 +4,9 @@ namespace Tests\Feature\JsonApi;
|
|||
|
||||
use App\Models\Tag;
|
||||
use Illuminate\Database\Eloquent\Factories\Factory;
|
||||
use Illuminate\Foundation\Testing\RefreshDatabase;
|
||||
|
||||
class TagApiTest extends JsonApiTestCase
|
||||
{
|
||||
use RefreshDatabase;
|
||||
|
||||
/**
|
||||
* @inheritdoc
|
||||
|
|
|
|||
|
|
@ -4,11 +4,9 @@ namespace Tests\Feature\JsonApi;
|
|||
|
||||
use App\Models\User;
|
||||
use Database\Factories\UserFactory;
|
||||
use Illuminate\Foundation\Testing\RefreshDatabase;
|
||||
|
||||
class UserApiTest extends JsonApiTestCase
|
||||
{
|
||||
use RefreshDatabase;
|
||||
|
||||
/**
|
||||
* @inheritdoc
|
||||
|
|
|
|||
|
|
@ -4,12 +4,10 @@ namespace Tests\Feature\Support;
|
|||
|
||||
use App\Models\Food;
|
||||
use App\Support\Nutrients;
|
||||
use Illuminate\Foundation\Testing\RefreshDatabase;
|
||||
use Tests\TestCase;
|
||||
|
||||
class NutrientsTest extends TestCase
|
||||
{
|
||||
use RefreshDatabase;
|
||||
|
||||
/**
|
||||
* Test invalid Food nutrient multiplier calculation.
|
||||
|
|
|
|||
|
|
@ -2,9 +2,11 @@
|
|||
|
||||
namespace Tests;
|
||||
|
||||
use Illuminate\Foundation\Testing\RefreshDatabase;
|
||||
use Illuminate\Foundation\Testing\TestCase as BaseTestCase;
|
||||
|
||||
abstract class TestCase extends BaseTestCase
|
||||
{
|
||||
use CreatesApplication;
|
||||
use RefreshDatabase;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,6 +1,5 @@
|
|||
<?php
|
||||
|
||||
|
||||
namespace Tests\Unit\Rules;
|
||||
|
||||
use App\Rules\ArrayNotEmpty;
|
||||
|
|
|
|||
|
|
@ -1,6 +1,5 @@
|
|||
<?php
|
||||
|
||||
|
||||
namespace Tests\Unit\Rules;
|
||||
|
||||
use App\Rules\InArray;
|
||||
|
|
|
|||
|
|
@ -1,6 +1,5 @@
|
|||
<?php
|
||||
|
||||
|
||||
namespace Tests\Unit\Rules;
|
||||
|
||||
use App\Rules\StringIsDecimalOrFraction;
|
||||
|
|
|
|||
|
|
@ -24,15 +24,15 @@ class NumberTest extends TestCase
|
|||
}
|
||||
|
||||
/**
|
||||
* Test (fraction) string to float conversion.
|
||||
* Test (rational) string to float conversion.
|
||||
*
|
||||
* @dataProvider fractionStringFloatsProvider
|
||||
*
|
||||
* @see \App\Support\Number::fractionStringFromFloat()
|
||||
* @see \App\Support\Number::rationalStringFromFloat()
|
||||
*/
|
||||
public function testFractionStringFromFloat(string $expectedString, float $float): void
|
||||
{
|
||||
$result = Number::fractionStringFromFloat($float);
|
||||
$result = Number::rationalStringFromFloat($float);
|
||||
$this->assertIsString($result);
|
||||
$this->assertEquals($expectedString, $result);
|
||||
}
|
||||
|
|
@ -62,9 +62,9 @@ class NumberTest extends TestCase
|
|||
*/
|
||||
public function fractionStringFloatsProvider(): array {
|
||||
return [
|
||||
['0', 0.0], ['1/8', 1/8], ['1/4', 1/4], ['1/2', 1/2],
|
||||
['3/4', 3/4], ['1', 1.0], ['1 1/4', 1.25],
|
||||
['1 1/2', 1.5], ['2 1/2', 2.5], ['2 3/4', 2.75],
|
||||
['0', 0.0], ['1/8', 1/8], ['1/4', 1/4], ['1/3', 1/3], ['1/2', 1/2],
|
||||
['2/3', 2/3], ['3/4', 3/4], ['1', 1.0], ['1 1/4', 1.25], ['1 1/3', 1 + 1/3],
|
||||
['1 1/2', 1.5], ['1 2/3', 1 + 2/3], ['2 1/2', 2.5], ['2 3/4', 2.75],
|
||||
];
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -0,0 +1,48 @@
|
|||
<?php
|
||||
|
||||
namespace Tests\Unit\Support;
|
||||
|
||||
use App\Support\Nutrients;
|
||||
use PHPUnit\Framework\TestCase;
|
||||
|
||||
class NutrientsTest extends TestCase
|
||||
{
|
||||
|
||||
/**
|
||||
* Test nutrient rounding.
|
||||
*
|
||||
* @dataProvider nutrientAmountsProvider
|
||||
*
|
||||
* @see \App\Support\Nutrients::round()
|
||||
*/
|
||||
public function testNutrientRounding(float $amount, string $nutrient, float $expectedFloat): void
|
||||
{
|
||||
$result = Nutrients::round($amount, $nutrient);
|
||||
$this->assertIsFloat($result);
|
||||
$this->assertEquals($expectedFloat, $result);
|
||||
}
|
||||
|
||||
public function testNutrientRoundingExcepts(): void
|
||||
{
|
||||
$this->expectException(\InvalidArgumentException::class);
|
||||
Nutrients::round(1, 'pancake');
|
||||
}
|
||||
|
||||
/**
|
||||
* Data providers.
|
||||
*/
|
||||
|
||||
/**
|
||||
* Provide nutrient amounts for and expected rounded results.
|
||||
*/
|
||||
public function nutrientAmountsProvider(): array {
|
||||
return [
|
||||
[0, 'calories', 0], [1, 'calories', 0], [2, 'calories', 0], [3, 'calories', 0], [4, 'calories', 0], [5, 'calories', 5], [21, 'calories', 20], [23, 'calories', 25], [45, 'calories', 45], [50, 'calories', 50],
|
||||
[0, 'carbohydrates', 0], [0.1, 'carbohydrates', 0], [0.2, 'carbohydrates', 0], [0.3, 'carbohydrates', 0], [0.4, 'carbohydrates', 0], [0.5, 'carbohydrates', 0], [0.9, 'carbohydrates', 0], [1, 'carbohydrates', 1], [2.25, 'carbohydrates', 2], [2.75, 'carbohydrates', 3],
|
||||
[0, 'protein', 0], [0.1, 'protein', 0], [0.2, 'protein', 0], [0.3, 'protein', 0], [0.4, 'protein', 0], [0.5, 'protein', 0], [0.9, 'protein', 0], [1, 'protein', 1], [2.25, 'protein', 2], [2.75, 'protein', 3],
|
||||
[0, 'cholesterol', 0], [0.1, 'cholesterol', 0], [0.2, 'cholesterol', 0], [0.3, 'cholesterol', 0], [0.4, 'cholesterol', 0], [0.5, 'cholesterol', 0.5], [0.9, 'cholesterol', 1], [1, 'cholesterol', 1], [1.2, 'cholesterol', 1], [1.4, 'cholesterol', 1.5], [5, 'cholesterol', 5], [6, 'cholesterol', 6], [6.25, 'cholesterol', 6], [6.75, 'cholesterol', 7],
|
||||
[0, 'fat', 0], [0.1, 'fat', 0], [0.2, 'fat', 0], [0.3, 'fat', 0], [0.4, 'fat', 0], [0.5, 'fat', 0.5], [0.9, 'fat', 1], [1, 'fat', 1], [1.2, 'fat', 1], [1.4, 'fat', 1.5], [5, 'fat', 5], [6, 'fat', 6], [6.25, 'fat', 6], [6.75, 'fat', 7],
|
||||
[0, 'sodium', 0], [1, 'sodium', 0], [2, 'sodium', 0], [3, 'sodium', 0], [4, 'sodium', 0], [5, 'sodium', 5], [6, 'sodium', 5], [7, 'sodium', 5], [8, 'sodium', 10], [9, 'sodium', 10], [10, 'sodium', 10], [139, 'sodium', 140], [140, 'sodium', 140], [146, 'sodium', 150], [151, 'sodium', 150], [159, 'sodium', 160],
|
||||
];
|
||||
}
|
||||
}
|
||||
Loading…
Reference in New Issue