mirror of https://github.com/kcal-app/kcal.git
Merge remote-tracking branch 'origin/main' into demo
This commit is contained in:
commit
0b1e4c0429
9
.env.ci
9
.env.ci
|
|
@ -1,5 +1,5 @@
|
||||||
APP_NAME=kcal
|
APP_NAME=kcal
|
||||||
APP_ENV=local
|
APP_ENV=testing
|
||||||
APP_KEY=
|
APP_KEY=
|
||||||
APP_DEBUG=true
|
APP_DEBUG=true
|
||||||
APP_URL=http://localhost
|
APP_URL=http://localhost
|
||||||
|
|
@ -7,7 +7,12 @@ APP_URL=http://localhost
|
||||||
LOG_CHANNEL=stack
|
LOG_CHANNEL=stack
|
||||||
LOG_LEVEL=debug
|
LOG_LEVEL=debug
|
||||||
|
|
||||||
DB_CONNECTION=sqlite
|
DB_CONNECTION=mysql
|
||||||
|
DB_HOST=127.0.0.1
|
||||||
|
DB_PORT=3306
|
||||||
|
DB_DATABASE=kcal
|
||||||
|
DB_USERNAME=root
|
||||||
|
DB_PASSWORD=root
|
||||||
|
|
||||||
SCOUT_DRIVER=elastic
|
SCOUT_DRIVER=elastic
|
||||||
ELASTIC_HOST=localhost:9200
|
ELASTIC_HOST=localhost:9200
|
||||||
|
|
|
||||||
|
|
@ -10,6 +10,10 @@ jobs:
|
||||||
test:
|
test:
|
||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
steps:
|
steps:
|
||||||
|
- uses: mirromutth/mysql-action@v1.1
|
||||||
|
with:
|
||||||
|
mysql database: kcal
|
||||||
|
mysql root password: root
|
||||||
- uses: shivammathur/setup-php@v2
|
- uses: shivammathur/setup-php@v2
|
||||||
with:
|
with:
|
||||||
php-version: '8.0'
|
php-version: '8.0'
|
||||||
|
|
@ -41,7 +45,7 @@ jobs:
|
||||||
php -r "file_exists('.env') || copy('.env.ci', '.env');"
|
php -r "file_exists('.env') || copy('.env.ci', '.env');"
|
||||||
php artisan key:generate
|
php artisan key:generate
|
||||||
- name: Run tests
|
- name: Run tests
|
||||||
run: vendor/bin/paratest --coverage-clover build/logs/clover.xml
|
run: php artisan test --parallel --recreate-databases --coverage-clover build/logs/clover.xml
|
||||||
- name: Upload coverage results to Coveralls
|
- name: Upload coverage results to Coveralls
|
||||||
env:
|
env:
|
||||||
COVERALLS_REPO_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
COVERALLS_REPO_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||||
|
|
|
||||||
2123
.phpstorm.meta.php
2123
.phpstorm.meta.php
File diff suppressed because it is too large
Load Diff
2037
_ide_helper.php
2037
_ide_helper.php
File diff suppressed because it is too large
Load Diff
|
|
@ -7,6 +7,7 @@ use App\Models\Recipe;
|
||||||
use App\Search\Ingredient;
|
use App\Search\Ingredient;
|
||||||
use ElasticScoutDriverPlus\Builders\MultiMatchQueryBuilder;
|
use ElasticScoutDriverPlus\Builders\MultiMatchQueryBuilder;
|
||||||
use ElasticScoutDriverPlus\Builders\TermsQueryBuilder;
|
use ElasticScoutDriverPlus\Builders\TermsQueryBuilder;
|
||||||
|
use ElasticScoutDriverPlus\Support\Query;
|
||||||
use Illuminate\Database\Eloquent\Collection;
|
use Illuminate\Database\Eloquent\Collection;
|
||||||
use Illuminate\Http\JsonResponse;
|
use Illuminate\Http\JsonResponse;
|
||||||
use Illuminate\Http\Request;
|
use Illuminate\Http\Request;
|
||||||
|
|
@ -42,11 +43,10 @@ class IngredientPickerController extends Controller
|
||||||
* Search using an ElasticSearch service.
|
* Search using an ElasticSearch service.
|
||||||
*/
|
*/
|
||||||
private function searchWithElasticSearch(string $term): Collection {
|
private function searchWithElasticSearch(string $term): Collection {
|
||||||
return Food::boolSearch()
|
$query = Query::bool()
|
||||||
->join(Recipe::class)
|
|
||||||
|
|
||||||
// Attempt to match exact phrase first.
|
// Attempt to match exact phrase first.
|
||||||
->should('match_phrase', ['name' => $term])
|
->should(Query::matchPhrase()->field('name')->query($term))
|
||||||
|
|
||||||
// Attempt multi-match search on all relevant fields with search-as-you-type on name.
|
// Attempt multi-match search on all relevant fields with search-as-you-type on name.
|
||||||
->should((new MultiMatchQueryBuilder())
|
->should((new MultiMatchQueryBuilder())
|
||||||
|
|
@ -57,10 +57,12 @@ class IngredientPickerController extends Controller
|
||||||
->fuzziness('AUTO'))
|
->fuzziness('AUTO'))
|
||||||
|
|
||||||
// Attempt to match on any tags in the term.
|
// Attempt to match on any tags in the term.
|
||||||
->should((new TermsQueryBuilder())
|
->should((new TermsQueryBuilder())->field('tags')->values(explode(' ', $term)))
|
||||||
->terms('tags', explode(' ', $term)))
|
|
||||||
|
|
||||||
// Get resulting models.
|
->minimumShouldMatch(1);
|
||||||
|
|
||||||
|
return Food::searchQuery($query)
|
||||||
|
->join(Recipe::class)
|
||||||
->execute()
|
->execute()
|
||||||
->models();
|
->models();
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -2,8 +2,8 @@
|
||||||
|
|
||||||
namespace App\Http\Middleware;
|
namespace App\Http\Middleware;
|
||||||
|
|
||||||
use Fideloper\Proxy\TrustProxies as Middleware;
|
|
||||||
use Illuminate\Http\Request;
|
use Illuminate\Http\Request;
|
||||||
|
use Illuminate\Http\Middleware\TrustProxies as Middleware;
|
||||||
|
|
||||||
class TrustProxies extends Middleware
|
class TrustProxies extends Middleware
|
||||||
{
|
{
|
||||||
|
|
@ -15,5 +15,10 @@ class TrustProxies extends Middleware
|
||||||
/**
|
/**
|
||||||
* {@inheritdoc}
|
* {@inheritdoc}
|
||||||
*/
|
*/
|
||||||
protected $headers = Request::HEADER_X_FORWARDED_ALL;
|
protected $headers =
|
||||||
|
Request::HEADER_X_FORWARDED_FOR |
|
||||||
|
Request::HEADER_X_FORWARDED_HOST |
|
||||||
|
Request::HEADER_X_FORWARDED_PORT |
|
||||||
|
Request::HEADER_X_FORWARDED_PROTO |
|
||||||
|
Request::HEADER_X_FORWARDED_AWS_ELB;
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -7,10 +7,9 @@ use App\Models\Traits\Journalable;
|
||||||
use App\Models\Traits\Sluggable;
|
use App\Models\Traits\Sluggable;
|
||||||
use App\Models\Traits\Taggable;
|
use App\Models\Traits\Taggable;
|
||||||
use App\Support\Number;
|
use App\Support\Number;
|
||||||
use ElasticScoutDriverPlus\QueryDsl;
|
use ElasticScoutDriverPlus\Searchable;
|
||||||
use Illuminate\Database\Eloquent\Factories\HasFactory;
|
use Illuminate\Database\Eloquent\Factories\HasFactory;
|
||||||
use Illuminate\Database\Eloquent\Model;
|
use Illuminate\Database\Eloquent\Model;
|
||||||
use Laravel\Scout\Searchable;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* App\Models\Food
|
* App\Models\Food
|
||||||
|
|
@ -80,7 +79,6 @@ final class Food extends Model
|
||||||
use HasFactory;
|
use HasFactory;
|
||||||
use Ingredient;
|
use Ingredient;
|
||||||
use Journalable;
|
use Journalable;
|
||||||
use QueryDsl;
|
|
||||||
use Searchable;
|
use Searchable;
|
||||||
use Sluggable;
|
use Sluggable;
|
||||||
use Taggable;
|
use Taggable;
|
||||||
|
|
|
||||||
|
|
@ -9,12 +9,11 @@ use App\Models\Traits\Sluggable;
|
||||||
use App\Models\Traits\Taggable;
|
use App\Models\Traits\Taggable;
|
||||||
use App\Support\Number;
|
use App\Support\Number;
|
||||||
use App\Support\Nutrients;
|
use App\Support\Nutrients;
|
||||||
use ElasticScoutDriverPlus\QueryDsl;
|
use ElasticScoutDriverPlus\Searchable;
|
||||||
use Illuminate\Database\Eloquent\Factories\HasFactory;
|
use Illuminate\Database\Eloquent\Factories\HasFactory;
|
||||||
use Illuminate\Database\Eloquent\Model;
|
use Illuminate\Database\Eloquent\Model;
|
||||||
use Illuminate\Database\Eloquent\Relations\HasMany;
|
use Illuminate\Database\Eloquent\Relations\HasMany;
|
||||||
use Illuminate\Support\Collection;
|
use Illuminate\Support\Collection;
|
||||||
use Laravel\Scout\Searchable;
|
|
||||||
use Spatie\Image\Manipulations;
|
use Spatie\Image\Manipulations;
|
||||||
use Spatie\MediaLibrary\HasMedia;
|
use Spatie\MediaLibrary\HasMedia;
|
||||||
use Spatie\MediaLibrary\InteractsWithMedia;
|
use Spatie\MediaLibrary\InteractsWithMedia;
|
||||||
|
|
@ -91,7 +90,6 @@ final class Recipe extends Model implements HasMedia
|
||||||
use Ingredient;
|
use Ingredient;
|
||||||
use InteractsWithMedia;
|
use InteractsWithMedia;
|
||||||
use Journalable;
|
use Journalable;
|
||||||
use QueryDsl;
|
|
||||||
use Searchable;
|
use Searchable;
|
||||||
use Sluggable;
|
use Sluggable;
|
||||||
use Taggable;
|
use Taggable;
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,20 @@
|
||||||
|
<?php
|
||||||
|
|
||||||
|
namespace App\Policies;
|
||||||
|
|
||||||
|
use App\Models\Goal;
|
||||||
|
use App\Models\User;
|
||||||
|
use Illuminate\Auth\Access\HandlesAuthorization;
|
||||||
|
|
||||||
|
class GoalPolicy
|
||||||
|
{
|
||||||
|
use HandlesAuthorization;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Determine whether the user can access (show, edit, delete) the goal.
|
||||||
|
*/
|
||||||
|
public function access(User $user, Goal $goal): bool {
|
||||||
|
return $user->id === $goal->user_id;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
@ -2,7 +2,9 @@
|
||||||
|
|
||||||
namespace App\Providers;
|
namespace App\Providers;
|
||||||
|
|
||||||
|
use App\Models\Goal;
|
||||||
use App\Models\User;
|
use App\Models\User;
|
||||||
|
use App\Policies\GoalPolicy;
|
||||||
use App\Policies\UserPolicy;
|
use App\Policies\UserPolicy;
|
||||||
use Illuminate\Foundation\Support\Providers\AuthServiceProvider as ServiceProvider;
|
use Illuminate\Foundation\Support\Providers\AuthServiceProvider as ServiceProvider;
|
||||||
|
|
||||||
|
|
@ -13,6 +15,7 @@ class AuthServiceProvider extends ServiceProvider
|
||||||
* @inheritdoc
|
* @inheritdoc
|
||||||
*/
|
*/
|
||||||
protected $policies = [
|
protected $policies = [
|
||||||
|
Goal::class => GoalPolicy::class,
|
||||||
User::class => UserPolicy::class,
|
User::class => UserPolicy::class,
|
||||||
];
|
];
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -9,39 +9,39 @@
|
||||||
"ext-gd": "*",
|
"ext-gd": "*",
|
||||||
"ext-json": "*",
|
"ext-json": "*",
|
||||||
"ext-mbstring": "*",
|
"ext-mbstring": "*",
|
||||||
"algolia/algoliasearch-client-php": "^2.7",
|
"algolia/algoliasearch-client-php": "^3.2",
|
||||||
"algolia/scout-extended": "^1.15",
|
"algolia/scout-extended": "^2.0",
|
||||||
"babenkoivan/elastic-migrations": "^1.4",
|
"babenkoivan/elastic-migrations": "^2.0",
|
||||||
"babenkoivan/elastic-scout-driver": "^1.3",
|
"babenkoivan/elastic-scout-driver": "^2.0",
|
||||||
"babenkoivan/elastic-scout-driver-plus": "^2.0",
|
"babenkoivan/elastic-scout-driver-plus": "^3.3",
|
||||||
"cloudcreativity/laravel-json-api": "^3.2",
|
"cloudcreativity/laravel-json-api": "^4.0",
|
||||||
"cviebrock/eloquent-sluggable": "^8.0",
|
"cviebrock/eloquent-sluggable": "^9.0",
|
||||||
"doctrine/dbal": "^3.0",
|
"doctrine/dbal": "^3.0",
|
||||||
"fakerphp/faker": "^1.14",
|
"fakerphp/faker": "^1.14",
|
||||||
"fideloper/proxy": "^4.4",
|
|
||||||
"fruitcake/laravel-cors": "^2.0",
|
"fruitcake/laravel-cors": "^2.0",
|
||||||
"guzzlehttp/guzzle": "^7.0.1",
|
"guzzlehttp/guzzle": "^7.0.1",
|
||||||
"laravel/framework": "^8.12",
|
"laravel/framework": "^9.0",
|
||||||
"laravel/scout": "^8.6",
|
"laravel/scout": "^9.0",
|
||||||
"laravel/tinker": "^2.5",
|
"laravel/tinker": "^2.7",
|
||||||
"league/flysystem-aws-s3-v3": "~1.0",
|
"league/flysystem-aws-s3-v3": "^3.0",
|
||||||
"phospr/fraction": "^1.2",
|
"phospr/fraction": "^1.2",
|
||||||
"spatie/laravel-csp": "^2.6",
|
"spatie/laravel-csp": "^2.6",
|
||||||
"spatie/laravel-medialibrary": "^9.0.0",
|
"spatie/laravel-medialibrary": "^10.0",
|
||||||
"spatie/laravel-tags": "^3.0"
|
"spatie/laravel-tags": "^4.0"
|
||||||
},
|
},
|
||||||
"require-dev": {
|
"require-dev": {
|
||||||
"barryvdh/laravel-ide-helper": "^2.9",
|
"barryvdh/laravel-ide-helper": "^2.9",
|
||||||
"brianium/paratest": "^6.2",
|
"brianium/paratest": "^6.2",
|
||||||
"cloudcreativity/json-api-testing": "^3.2",
|
"cloudcreativity/json-api-testing": "^4.0",
|
||||||
"facade/ignition": "^2.5",
|
"fakerphp/faker": "^1.9.1",
|
||||||
"laravel/breeze": "^1.0",
|
"laravel/breeze": "^1.0",
|
||||||
"laravel/sail": "^1.10",
|
"laravel/sail": "^1.10",
|
||||||
"mockery/mockery": "^1.4.2",
|
"mockery/mockery": "^1.4.2",
|
||||||
"nunomaduro/collision": "^5.0",
|
"nunomaduro/collision": "^6.1",
|
||||||
"nunomaduro/larastan": "^0.6.13",
|
"nunomaduro/larastan": "^2.0",
|
||||||
"php-coveralls/php-coveralls": "^2.4",
|
"php-coveralls/php-coveralls": "^2.4",
|
||||||
"phpunit/phpunit": "^9.3.3"
|
"phpunit/phpunit": "^9.3.3",
|
||||||
|
"spatie/laravel-ignition": "^1.0"
|
||||||
},
|
},
|
||||||
"config": {
|
"config": {
|
||||||
"optimize-autoloader": true,
|
"optimize-autoloader": true,
|
||||||
|
|
|
||||||
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
16
package.json
16
package.json
|
|
@ -10,18 +10,18 @@
|
||||||
"production": "cross-env NODE_ENV=production node_modules/webpack/bin/webpack.js --config=node_modules/laravel-mix/setup/webpack.config.js"
|
"production": "cross-env NODE_ENV=production node_modules/webpack/bin/webpack.js --config=node_modules/laravel-mix/setup/webpack.config.js"
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"@tailwindcss/forms": "^0.3.3",
|
"@tailwindcss/forms": "^0.4.0",
|
||||||
"@tailwindcss/typography": "^0.4.1",
|
"@tailwindcss/typography": "^0.5.1",
|
||||||
"alpinejs": "^3.4.1",
|
"alpinejs": "^3.9.0",
|
||||||
"autoprefixer": "^10.3.6",
|
"autoprefixer": "^10.4.2",
|
||||||
"axios": "^0.21.4",
|
"axios": "^0.25.0",
|
||||||
"cross-env": "^7.0",
|
"cross-env": "^7.0",
|
||||||
"laravel-mix": "^6.0.31",
|
"laravel-mix": "^6.0.41",
|
||||||
"lodash": "^4.17.21",
|
"lodash": "^4.17.21",
|
||||||
"postcss-import": "^14.0.2",
|
"postcss-import": "^14.0.2",
|
||||||
"quill": "^1.3.7",
|
"quill": "^1.3.7",
|
||||||
"resolve-url-loader": "^4.0.0",
|
"resolve-url-loader": "^5.0.0",
|
||||||
"tailwindcss": "^2.2.16",
|
"tailwindcss": "^3.0.22",
|
||||||
"vue-template-compiler": "^2.6.14"
|
"vue-template-compiler": "^2.6.14"
|
||||||
},
|
},
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
|
|
|
||||||
|
|
@ -19,6 +19,7 @@
|
||||||
</coverage>
|
</coverage>
|
||||||
<php>
|
<php>
|
||||||
<server name="APP_ENV" value="testing"/>
|
<server name="APP_ENV" value="testing"/>
|
||||||
|
<server name="DB_USERNAME" value="root"/>
|
||||||
<server name="BCRYPT_ROUNDS" value="4"/>
|
<server name="BCRYPT_ROUNDS" value="4"/>
|
||||||
<server name="CACHE_DRIVER" value="array"/>
|
<server name="CACHE_DRIVER" value="array"/>
|
||||||
<server name="MAIL_MAILER" value="array"/>
|
<server name="MAIL_MAILER" value="array"/>
|
||||||
|
|
@ -26,9 +27,5 @@
|
||||||
<server name="SESSION_DRIVER" value="elastic"/>
|
<server name="SESSION_DRIVER" value="elastic"/>
|
||||||
<server name="SESSION_DRIVER" value="array"/>
|
<server name="SESSION_DRIVER" value="array"/>
|
||||||
<server name="TELESCOPE_ENABLED" value="false"/>
|
<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>
|
</php>
|
||||||
</phpunit>
|
</phpunit>
|
||||||
|
|
|
||||||
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
|
|
@ -1,7 +1,7 @@
|
||||||
@props(['status'])
|
@props(['status'])
|
||||||
|
|
||||||
@if ($status)
|
@if ($status)
|
||||||
<div {{ $attributes->merge(['class' => 'font-medium text-sm text-green-600']) }}>
|
<div {{ $attributes->merge(['class' => 'font-medium text-sm text-emerald-600']) }}>
|
||||||
{{ $status }}
|
{{ $status }}
|
||||||
</div>
|
</div>
|
||||||
@endif
|
@endif
|
||||||
|
|
|
||||||
|
|
@ -1,3 +1,3 @@
|
||||||
<x-button-link.base :attributes="$attributes" class="text-white bg-green-800 hover:bg-green-700 active:bg-green-900 focus:border-green-900 ring-green-300">
|
<x-button-link.base :attributes="$attributes" class="text-white bg-emerald-800 hover:bg-emerald-700 active:bg-emerald-900 focus:border-emerald-900 ring-emerald-300">
|
||||||
{{ $slot }}
|
{{ $slot }}
|
||||||
</x-button-link.base>
|
</x-button-link.base>
|
||||||
|
|
|
||||||
|
|
@ -28,11 +28,11 @@ switch ($width) {
|
||||||
|
|
||||||
<div x-show="open"
|
<div x-show="open"
|
||||||
x-transition:enter="transition ease-out duration-200"
|
x-transition:enter="transition ease-out duration-200"
|
||||||
x-transition:enter-start="transform opacity-0 scale-95"
|
x-transition:enter-start="opacity-0 scale-95"
|
||||||
x-transition:enter-end="transform opacity-100 scale-100"
|
x-transition:enter-end="opacity-100 scale-100"
|
||||||
x-transition:leave="transition ease-in duration-75"
|
x-transition:leave="transition ease-in duration-75"
|
||||||
x-transition:leave-start="transform opacity-100 scale-100"
|
x-transition:leave-start="opacity-100 scale-100"
|
||||||
x-transition:leave-end="transform opacity-0 scale-95"
|
x-transition:leave-end="opacity-0 scale-95"
|
||||||
class="absolute z-50 mt-2 {{ $width }} rounded-md shadow-lg {{ $alignmentClasses }}"
|
class="absolute z-50 mt-2 {{ $width }} rounded-md shadow-lg {{ $alignmentClasses }}"
|
||||||
style="display: none;"
|
style="display: none;"
|
||||||
@click="open = false">
|
@click="open = false">
|
||||||
|
|
|
||||||
|
|
@ -26,7 +26,7 @@
|
||||||
<div class="absolute border-2 border-gray-500 border-b-0 bg-white"
|
<div class="absolute border-2 border-gray-500 border-b-0 bg-white"
|
||||||
x-bind="ingredient">
|
x-bind="ingredient">
|
||||||
<template x-for="result in results" :key="result.id">
|
<template x-for="result in results" :key="result.id">
|
||||||
<div class="p-1 border-b-2 border-gray-500 hover:bg-yellow-300 cursor-pointer" x-bind:data-id="result.id">
|
<div class="p-1 border-b-2 border-gray-500 hover:bg-amber-300 cursor-pointer" x-bind:data-id="result.id">
|
||||||
<div class="pointer-events-none">
|
<div class="pointer-events-none">
|
||||||
<div>
|
<div>
|
||||||
<span class="font-bold" x-text="result.name"></span><span class="text-gray-600" x-text="', ' + result.detail" x-show="result.detail"></span>
|
<span class="font-bold" x-text="result.name"></span><span class="text-gray-600" x-text="', ' + result.detail" x-show="result.detail"></span>
|
||||||
|
|
|
||||||
|
|
@ -1,3 +1,3 @@
|
||||||
<button {{ $attributes->merge(['type' => 'submit', 'class' => "inline-flex items-center border border-transparent rounded-md font-semibold text-xs text-green-500 tracking-widest hover:text-green-700 active:text-green-900 focus:outline-none focus:border-green-900 focus:ring ring-green-300 disabled:opacity-25 transition ease-in-out duration-150"]) }}>
|
<button {{ $attributes->merge(['type' => 'submit', 'class' => "inline-flex items-center border border-transparent rounded-md font-semibold text-xs text-emerald-500 tracking-widest hover:text-emerald-700 active:text-emerald-900 focus:outline-none focus:border-emerald-900 focus:ring ring-emerald-300 disabled:opacity-25 transition ease-in-out duration-150"]) }}>
|
||||||
{{ $slot }}
|
{{ $slot }}
|
||||||
</button>
|
</button>
|
||||||
|
|
|
||||||
|
|
@ -1,4 +1,4 @@
|
||||||
<div x-data data-tags="{{ $defaultTags ?? '[]' }}">
|
<div x-data data-tags="{!! $defaultTags ?? '[]' !!}">
|
||||||
<div x-data="tagSelect()" @click.outside="clearSearch()" @keydown.escape="clearSearch()">
|
<div x-data="tagSelect()" @click.outside="clearSearch()" @keydown.escape="clearSearch()">
|
||||||
<div class="relative" @keydown.enter.prevent="addTag(searchTerm)">
|
<div class="relative" @keydown.enter.prevent="addTag(searchTerm)">
|
||||||
<x-inputs.input type="hidden"
|
<x-inputs.input type="hidden"
|
||||||
|
|
|
||||||
|
|
@ -25,7 +25,7 @@
|
||||||
@endif
|
@endif
|
||||||
@if(!$food->ingredientAmountRelationships->isEmpty())
|
@if(!$food->ingredientAmountRelationships->isEmpty())
|
||||||
<div class="flex space-x-2 items-center text-lg">
|
<div class="flex space-x-2 items-center text-lg">
|
||||||
<div class="text-yellow-500">
|
<div class="text-amber-500">
|
||||||
<svg class="h-8 w-8" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 20 20" fill="currentColor">
|
<svg class="h-8 w-8" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 20 20" fill="currentColor">
|
||||||
<path fill-rule="evenodd" d="M10 1.944A11.954 11.954 0 012.166 5C2.056 5.649 2 6.319 2 7c0 5.225 3.34 9.67 8 11.317C14.66 16.67 18 12.225 18 7c0-.682-.057-1.35-.166-2.001A11.954 11.954 0 0110 1.944zM11 14a1 1 0 11-2 0 1 1 0 012 0zm0-7a1 1 0 10-2 0v3a1 1 0 102 0V7z" clip-rule="evenodd" />
|
<path fill-rule="evenodd" d="M10 1.944A11.954 11.954 0 012.166 5C2.056 5.649 2 6.319 2 7c0 5.225 3.34 9.67 8 11.317C14.66 16.67 18 12.225 18 7c0-.682-.057-1.35-.166-2.001A11.954 11.954 0 0110 1.944zM11 14a1 1 0 11-2 0 1 1 0 012 0zm0-7a1 1 0 10-2 0v3a1 1 0 102 0V7z" clip-rule="evenodd" />
|
||||||
</svg>
|
</svg>
|
||||||
|
|
|
||||||
|
|
@ -125,7 +125,7 @@
|
||||||
:selectedValue="$currentGoal?->id ?? null">
|
:selectedValue="$currentGoal?->id ?? null">
|
||||||
</x-inputs.select>
|
</x-inputs.select>
|
||||||
<div class="flex items-center justify-start mt-4">
|
<div class="flex items-center justify-start mt-4">
|
||||||
<x-inputs.button class="bg-green-800 hover:bg-green-700">Change Goal</x-inputs.button>
|
<x-inputs.button class="bg-emerald-800 hover:bg-emerald-700">Change Goal</x-inputs.button>
|
||||||
<x-button-link.red class="ml-3" x-on:click="showGoalChangeForm = !showGoalChangeForm">
|
<x-button-link.red class="ml-3" x-on:click="showGoalChangeForm = !showGoalChangeForm">
|
||||||
Cancel
|
Cancel
|
||||||
</x-button-link.red>
|
</x-button-link.red>
|
||||||
|
|
|
||||||
|
|
@ -41,7 +41,7 @@
|
||||||
<!-- Page Content -->
|
<!-- Page Content -->
|
||||||
<main>
|
<main>
|
||||||
@if(session()->has('message'))
|
@if(session()->has('message'))
|
||||||
<div class="bg-green-200 p-2 mb-2">
|
<div class="bg-emerald-200 p-2 mb-2">
|
||||||
{{ session()->get('message') }}
|
{{ session()->get('message') }}
|
||||||
</div>
|
</div>
|
||||||
@endif
|
@endif
|
||||||
|
|
|
||||||
|
|
@ -13,7 +13,7 @@
|
||||||
<div class="flex flex-col space-y-2 mt-2 text-lg">
|
<div class="flex flex-col space-y-2 mt-2 text-lg">
|
||||||
@if(!$recipe->ingredientAmountRelationships->isEmpty())
|
@if(!$recipe->ingredientAmountRelationships->isEmpty())
|
||||||
<div class="flex space-x-2 items-center text-lg">
|
<div class="flex space-x-2 items-center text-lg">
|
||||||
<div class="text-yellow-500">
|
<div class="text-amber-500">
|
||||||
<svg class="h-8 w-8" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 20 20" fill="currentColor">
|
<svg class="h-8 w-8" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 20 20" fill="currentColor">
|
||||||
<path fill-rule="evenodd" d="M10 1.944A11.954 11.954 0 012.166 5C2.056 5.649 2 6.319 2 7c0 5.225 3.34 9.67 8 11.317C14.66 16.67 18 12.225 18 7c0-.682-.057-1.35-.166-2.001A11.954 11.954 0 0110 1.944zM11 14a1 1 0 11-2 0 1 1 0 012 0zm0-7a1 1 0 10-2 0v3a1 1 0 102 0V7z" clip-rule="evenodd" />
|
<path fill-rule="evenodd" d="M10 1.944A11.954 11.954 0 012.166 5C2.056 5.649 2 6.319 2 7c0 5.225 3.34 9.67 8 11.317C14.66 16.67 18 12.225 18 7c0-.682-.057-1.35-.166-2.001A11.954 11.954 0 0110 1.944zM11 14a1 1 0 11-2 0 1 1 0 012 0zm0-7a1 1 0 10-2 0v3a1 1 0 102 0V7z" clip-rule="evenodd" />
|
||||||
</svg>
|
</svg>
|
||||||
|
|
|
||||||
|
|
@ -133,7 +133,7 @@
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<x-inputs.button type="button"
|
<x-inputs.button type="button"
|
||||||
class="bg-green-800 hover:bg-green-700 active:bg-green-900 focus:border-green-900 ring-green-300"
|
class="bg-emerald-800 hover:bg-emerald-700 active:bg-emerald-900 focus:border-emerald-900 ring-emerald-300"
|
||||||
x-on:click="addNodeFromTemplate($refs.ingredients, 'ingredient');">
|
x-on:click="addNodeFromTemplate($refs.ingredients, 'ingredient');">
|
||||||
Add Ingredient
|
Add Ingredient
|
||||||
</x-inputs.button>
|
</x-inputs.button>
|
||||||
|
|
@ -158,7 +158,7 @@
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<x-inputs.button type="button"
|
<x-inputs.button type="button"
|
||||||
class="bg-green-800 hover:bg-green-700 active:bg-green-900 focus:border-green-900 ring-green-300"
|
class="bg-emerald-800 hover:bg-emerald-700 active:bg-emerald-900 focus:border-emerald-900 ring-emerald-300"
|
||||||
x-on:click="addNodeFromTemplate($refs.steps, 'step');">
|
x-on:click="addNodeFromTemplate($refs.steps, 'step');">
|
||||||
Add Step
|
Add Step
|
||||||
</x-inputs.button>
|
</x-inputs.button>
|
||||||
|
|
|
||||||
|
|
@ -43,8 +43,18 @@
|
||||||
@if($item::class === \App\Models\IngredientAmount::class)
|
@if($item::class === \App\Models\IngredientAmount::class)
|
||||||
<li>
|
<li>
|
||||||
<span>
|
<span>
|
||||||
{{ \App\Support\Number::rationalStringFromFloat($item->amount) }}
|
{{-- Prevent food with serving size > 1 from incorrectly using formatted
|
||||||
@if($item->unitFormatted){{ $item->unitFormatted }}@endif
|
serving unit with number of servings. E.g., for a recipe calling for 1
|
||||||
|
serving of a food with 4 tbsp. to a serving size show "1 serving" instead
|
||||||
|
of "1 tbsp." (incorrect). --}}
|
||||||
|
@if($item->unit === 'serving' && $item->ingredient->serving_size > 1 && ($item->ingredient->serving_unit || $item->ingredient->serving_unit_name))
|
||||||
|
{{ \App\Support\Number::rationalStringFromFloat($item->amount * $item->ingredient->serving_size) }} {{ $item->unitFormatted }}
|
||||||
|
<span class="text-gray-500">({{ \App\Support\Number::rationalStringFromFloat($item->amount) }} {{ \Illuminate\Support\Str::plural('serving', $item->amount ) }})</span>
|
||||||
|
@else
|
||||||
|
{{ \App\Support\Number::rationalStringFromFloat($item->amount) }}
|
||||||
|
@if($item->unitFormatted){{ $item->unitFormatted }}@endif
|
||||||
|
@endif
|
||||||
|
|
||||||
@if($item->ingredient->type === \App\Models\Recipe::class)
|
@if($item->ingredient->type === \App\Models\Recipe::class)
|
||||||
<a class="text-gray-500 hover:text-gray-700 hover:border-gray-300"
|
<a class="text-gray-500 hover:text-gray-700 hover:border-gray-300"
|
||||||
href="{{ route('recipes.show', $item->ingredient) }}">
|
href="{{ route('recipes.show', $item->ingredient) }}">
|
||||||
|
|
|
||||||
|
|
@ -26,8 +26,9 @@ Route::middleware(['auth'])->group(function () {
|
||||||
Route::get('/foods/{food}/delete', [FoodController::class, 'delete'])->name('foods.delete');
|
Route::get('/foods/{food}/delete', [FoodController::class, 'delete'])->name('foods.delete');
|
||||||
|
|
||||||
// Goals.
|
// Goals.
|
||||||
Route::resource('goals', GoalController::class);
|
Route::resource('goals', GoalController::class)->only(['index', 'create', 'store']);
|
||||||
Route::get('/goals/{goal}/delete', [GoalController::class, 'delete'])->name('goals.delete');
|
Route::resource('goals', GoalController::class)->except(['index', 'create', 'store'])->middleware(['can:access,goal']);
|
||||||
|
Route::get('/goals/{goal}/delete', [GoalController::class, 'delete'])->middleware(['can:access,goal'])->name('goals.delete');
|
||||||
|
|
||||||
// Ingredient picker.
|
// Ingredient picker.
|
||||||
Route::get('/ingredient-picker/search', [IngredientPickerController::class, 'search'])->name('ingredient-picker.search');
|
Route::get('/ingredient-picker/search', [IngredientPickerController::class, 'search'])->name('ingredient-picker.search');
|
||||||
|
|
@ -51,10 +52,8 @@ Route::middleware(['auth'])->group(function () {
|
||||||
|
|
||||||
// Users.
|
// Users.
|
||||||
Route::get('/profile/{user}', [ProfileController::class, 'show'])->name('profiles.show');
|
Route::get('/profile/{user}', [ProfileController::class, 'show'])->name('profiles.show');
|
||||||
});
|
|
||||||
|
|
||||||
Route::middleware(['auth', 'can:editProfile,user'])->group(function () {
|
|
||||||
// Profiles (non-admin Users variant).
|
// Profiles (non-admin Users variant).
|
||||||
Route::get('/profile/{user}/edit', [ProfileController::class, 'edit'])->name('profiles.edit');
|
Route::get('/profile/{user}/edit', [ProfileController::class, 'edit'])->middleware(['can:editProfile,user'])->name('profiles.edit');
|
||||||
Route::put('/profile/{user}', [ProfileController::class, 'update'])->name('profiles.update');
|
Route::put('/profile/{user}', [ProfileController::class, 'update'])->middleware(['can:editProfile,user'])->name('profiles.update');
|
||||||
});
|
});
|
||||||
|
|
|
||||||
|
|
@ -15,18 +15,36 @@ use Illuminate\Support\Facades\Artisan;
|
||||||
*/
|
*/
|
||||||
|
|
||||||
if (!App::isProduction()) {
|
if (!App::isProduction()) {
|
||||||
|
/**
|
||||||
|
* Clear all caches.
|
||||||
|
*/
|
||||||
|
Artisan::command('dev:cache-clear', function () {
|
||||||
|
/** @phpstan-ignore-next-line */
|
||||||
|
assert($this instanceof ClosureCommand);
|
||||||
|
$commands = [
|
||||||
|
'cache:clear',
|
||||||
|
'config:clear',
|
||||||
|
'route:clear',
|
||||||
|
'view:clear',
|
||||||
|
];
|
||||||
|
foreach ($commands as $command) {
|
||||||
|
Artisan::call($command);
|
||||||
|
$this->info(trim(Artisan::output()));
|
||||||
|
}
|
||||||
|
$this->info('All caches cleared!');
|
||||||
|
})->purpose('Clear all caches.');
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Wipe, migrate, and seed the database.
|
* Wipe, migrate, and seed the database.
|
||||||
*/
|
*/
|
||||||
Artisan::command('dev:reset', function () {
|
Artisan::command('dev:reset', function () {
|
||||||
/** @phpstan-ignore-next-line */
|
/** @phpstan-ignore-next-line */
|
||||||
assert($this instanceof ClosureCommand);
|
assert($this instanceof ClosureCommand);
|
||||||
Artisan::call('db:wipe');
|
$commands = ['db:wipe', 'migrate', 'db:seed'];
|
||||||
$this->info(Artisan::output());
|
foreach ($commands as $command) {
|
||||||
Artisan::call('migrate');
|
Artisan::call($command);
|
||||||
$this->info(Artisan::output());
|
$this->info(trim(Artisan::output()));
|
||||||
Artisan::call('db:seed');
|
}
|
||||||
$this->info(Artisan::output());
|
|
||||||
$this->info('Database reset complete!');
|
$this->info('Database reset complete!');
|
||||||
})->purpose('Wipe, migrate, and seed the database.');
|
})->purpose('Wipe, migrate, and seed the database.');
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,9 +1,8 @@
|
||||||
const defaultTheme = require('tailwindcss/defaultTheme');
|
const defaultTheme = require('tailwindcss/defaultTheme');
|
||||||
|
|
||||||
module.exports = {
|
module.exports = {
|
||||||
mode: 'jit',
|
|
||||||
|
|
||||||
purge: [
|
content: [
|
||||||
'./storage/framework/views/*.php',
|
'./storage/framework/views/*.php',
|
||||||
'./resources/views/**/*.blade.php',
|
'./resources/views/**/*.blade.php',
|
||||||
],
|
],
|
||||||
|
|
@ -16,12 +15,6 @@ module.exports = {
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
|
||||||
variants: {
|
|
||||||
extend: {
|
|
||||||
opacity: ['disabled'],
|
|
||||||
},
|
|
||||||
},
|
|
||||||
|
|
||||||
plugins: [
|
plugins: [
|
||||||
require('@tailwindcss/typography'),
|
require('@tailwindcss/typography'),
|
||||||
require('@tailwindcss/forms')
|
require('@tailwindcss/forms')
|
||||||
|
|
|
||||||
|
|
@ -2,6 +2,7 @@
|
||||||
|
|
||||||
namespace Tests\Feature\Http\Controllers;
|
namespace Tests\Feature\Http\Controllers;
|
||||||
|
|
||||||
|
use Algolia\AlgoliaSearch\Exceptions\UnreachableException;
|
||||||
use App\Http\Controllers\IngredientPickerController;
|
use App\Http\Controllers\IngredientPickerController;
|
||||||
use App\Models\Food;
|
use App\Models\Food;
|
||||||
use App\Models\Recipe;
|
use App\Models\Recipe;
|
||||||
|
|
@ -42,8 +43,8 @@ class IngredientPickerControllerTest extends LoggedInTestCase
|
||||||
*/
|
*/
|
||||||
public function testCanSearchWithAlgolia(): void
|
public function testCanSearchWithAlgolia(): void
|
||||||
{
|
{
|
||||||
$this->expectException(ConnectException::class);
|
$this->expectException(UnreachableException::class);
|
||||||
$this->expectExceptionMessageMatches("/Could not resolve host: \-dsn\.algolia\.net/");
|
$this->expectExceptionMessage("Impossible to connect, please check your Algolia Application Id.");
|
||||||
|
|
||||||
Config::set('scout.driver', 'algolia');
|
Config::set('scout.driver', 'algolia');
|
||||||
$response = $this->get($this->buildUrl(['term' => 'butter']));
|
$response = $this->get($this->buildUrl(['term' => 'butter']));
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue