diff --git a/CHANGELOG.md b/CHANGELOG.md index bf614e0..013c89c 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2,6 +2,14 @@ All notable changes to this project will be documented in this file. +## [3.14.1] - 2026-01-26 + +### Changed + +- **Variant Filter Logic** - Recipe variants are now shown by default with an option to hide them (previously hidden by default) + - Renamed filter from "Show Canonical Recipes" to "Hide Variants" for better clarity + - Users see all recipes including variants unless they explicitly enable the hide filter + ## [3.14.0] - 2026-01-25 ### Added diff --git a/app/Livewire/Web/Concerns/WithFilterShareTrait.php b/app/Livewire/Web/Concerns/WithFilterShareTrait.php index 3e912d6..671b167 100644 --- a/app/Livewire/Web/Concerns/WithFilterShareTrait.php +++ b/app/Livewire/Web/Concerns/WithFilterShareTrait.php @@ -15,7 +15,7 @@ * @property string $search * @property string $sortBy * @property bool $filterHasPdf - * @property bool $filterShowCanonical + * @property bool $filterHideVariants * @property array $excludedAllergenIds * @property array $ingredientIds * @property string $ingredientMatchMode @@ -167,8 +167,8 @@ protected function collectBooleanFilters(array &$filters): void $filters['has_pdf'] = true; } - if ($this->filterShowCanonical) { - $filters['show_canonical'] = true; + if ($this->filterHideVariants) { + $filters['hide_variants'] = true; } $filters['ingredient_match'] = $this->ingredientMatchMode; diff --git a/app/Livewire/Web/Recipes/RecipeIndex.php b/app/Livewire/Web/Recipes/RecipeIndex.php index e35ec32..9a30624 100644 --- a/app/Livewire/Web/Recipes/RecipeIndex.php +++ b/app/Livewire/Web/Recipes/RecipeIndex.php @@ -45,7 +45,7 @@ class RecipeIndex extends AbstractComponent public bool $filterHasPdf = false; - public bool $filterShowCanonical = false; + public bool $filterHideVariants = false; /** @var array */ public array $excludedAllergenIds = []; @@ -92,7 +92,7 @@ public function mount(?Menu $menu = null): void $this->selectedMenuWeek = $this->menu?->year_week; $this->viewMode = session('view_mode', ViewModeEnum::Grid->value); $this->filterHasPdf = session($this->filterSessionKey('has_pdf'), false); - $this->filterShowCanonical = session($this->filterSessionKey('show_canonical'), false); + $this->filterHideVariants = session($this->filterSessionKey('hide_variants'), false); $this->excludedAllergenIds = session($this->filterSessionKey('excluded_allergens'), []); $this->ingredientIds = session($this->filterSessionKey('ingredients'), []); $this->ingredientMatchMode = session($this->filterSessionKey('ingredient_match'), IngredientMatchModeEnum::Any->value); @@ -281,7 +281,7 @@ protected function getSessionMapping(): array { return [ 'filterHasPdf' => 'has_pdf', - 'filterShowCanonical' => 'show_canonical', + 'filterHideVariants' => 'hide_variants', 'excludedAllergenIds' => 'excluded_allergens', 'ingredientIds' => 'ingredients', 'ingredientMatchMode' => 'ingredient_match', @@ -308,7 +308,7 @@ public function activeFilterCount(): int $count++; } - if ($this->filterShowCanonical) { + if ($this->filterHideVariants) { $count++; } @@ -387,7 +387,7 @@ public function isTagActive(int $tagId): bool public function clearFilters(): void { $this->filterHasPdf = false; - $this->filterShowCanonical = false; + $this->filterHideVariants = false; $this->excludedAllergenIds = []; $this->ingredientIds = []; $this->ingredientMatchMode = IngredientMatchModeEnum::Any->value; @@ -402,7 +402,7 @@ public function clearFilters(): void session()->forget([ $this->filterSessionKey('has_pdf'), - $this->filterSessionKey('show_canonical'), + $this->filterSessionKey('hide_variants'), $this->filterSessionKey('excluded_allergens'), $this->filterSessionKey('ingredients'), $this->filterSessionKey('ingredient_match'), @@ -432,7 +432,7 @@ public function recipes(): LengthAwarePaginator ->when($menuRecipeIds !== [], fn (Builder $query) => $query->whereIn('id', $menuRecipeIds)) ->when($this->search !== '', fn (Builder $query): Builder => $this->applySearchFilter($query)) ->when($this->filterHasPdf, fn (Builder $query) => $query->where('has_pdf', true)) - ->when(! $this->filterShowCanonical, fn (Builder $query) => $query->where('variant', false)) + ->when($this->filterHideVariants, fn (Builder $query) => $query->where('variant', false)) ->when($this->excludedAllergenIds !== [], fn (Builder $query) => $query->whereDoesntHave( 'allergens', fn (Builder $allergenQuery) => $allergenQuery->whereIn('allergens.id', $this->excludedAllergenIds) diff --git a/app/Livewire/Web/Recipes/RecipeRandom.php b/app/Livewire/Web/Recipes/RecipeRandom.php index 2a23e85..2ae1ddd 100644 --- a/app/Livewire/Web/Recipes/RecipeRandom.php +++ b/app/Livewire/Web/Recipes/RecipeRandom.php @@ -25,7 +25,7 @@ public function randomRecipes(): Collection return Recipe::where('country_id', $this->countryId) ->when($this->search !== '', fn (Builder $query): Builder => $this->applySearchFilter($query)) ->when($this->filterHasPdf, fn (Builder $query) => $query->where('has_pdf', true)) - ->unless($this->filterShowCanonical, fn (Builder $query) => $query->where('variant', false)) + ->when($this->filterHideVariants, fn (Builder $query) => $query->where('variant', false)) ->when($this->excludedAllergenIds !== [], fn (Builder $query) => $query->whereDoesntHave( 'allergens', fn (Builder $allergenQuery) => $allergenQuery->whereIn('allergens.id', $this->excludedAllergenIds) diff --git a/lang/da.json b/lang/da.json index dc5836e..a026ebf 100644 --- a/lang/da.json +++ b/lang/da.json @@ -144,7 +144,7 @@ "Not Found": "Ikke fundet", "Oldest first": "Ældste først", "Only the owner can share this list.": "Kun ejeren kan dele denne liste.", - "Show Canonical Recipes": "Vis også opskriftsvarianter", + "Hide Variants": "Skjul varianter", "Only with PDF": "Kun med PDF", "Optional description for your list": "Valgfri beskrivelse til din liste", "Page Expired": "Siden er udløbet", diff --git a/lang/de.json b/lang/de.json index 354b551..e0a51b7 100644 --- a/lang/de.json +++ b/lang/de.json @@ -144,7 +144,7 @@ "Nutrition": "Nährwerte", "Oldest first": "Älteste zuerst", "Only the owner can share this list.": "Nur der Eigentümer kann diese Liste teilen.", - "Show Canonical Recipes": "Auch Rezeptvarianten anzeigen", + "Hide Variants": "Rezeptvarianten ausblenden", "Only with PDF": "Nur mit PDF", "Optional description for your list": "Optionale Beschreibung für deine Liste", "Page Expired": "Seite abgelaufen", diff --git a/lang/en.json b/lang/en.json index 1bbdd63..059bfa3 100644 --- a/lang/en.json +++ b/lang/en.json @@ -143,7 +143,7 @@ "Nutrition": "Nutrition", "Oldest first": "Oldest first", "Only the owner can share this list.": "Only the owner can share this list.", - "Show Canonical Recipes": "Include Recipe Variants", + "Hide Variants": "Hide Variants", "Only with PDF": "Only with PDF", "Optional description for your list": "Optional description for your list", "Page Expired": "Page Expired", diff --git a/lang/es.json b/lang/es.json index 768308a..1306560 100644 --- a/lang/es.json +++ b/lang/es.json @@ -144,7 +144,7 @@ "Not Found": "No encontrado", "Oldest first": "Más antiguas primero", "Only the owner can share this list.": "Solo el propietario puede compartir esta lista.", - "Show Canonical Recipes": "Incluir variantes de recetas", + "Hide Variants": "Ocultar variantes", "Only with PDF": "Solo con PDF", "Optional description for your list": "Descripción opcional para tu lista", "Page Expired": "Página expirada", diff --git a/lang/fr.json b/lang/fr.json index 96c41f7..8286672 100644 --- a/lang/fr.json +++ b/lang/fr.json @@ -144,7 +144,7 @@ "Not Found": "Non trouvé", "Oldest first": "Plus anciennes d'abord", "Only the owner can share this list.": "Seul le propriétaire peut partager cette liste.", - "Show Canonical Recipes": "Inclure les variantes de recettes", + "Hide Variants": "Masquer les variantes", "Only with PDF": "Uniquement avec PDF", "Optional description for your list": "Description optionnelle pour votre liste", "Page Expired": "Page expirée", diff --git a/lang/it.json b/lang/it.json index cab0faf..64cb1da 100644 --- a/lang/it.json +++ b/lang/it.json @@ -144,7 +144,7 @@ "Not Found": "Non trovato", "Oldest first": "Più vecchie prima", "Only the owner can share this list.": "Solo il proprietario può condividere questa lista.", - "Show Canonical Recipes": "Includi varianti delle ricette", + "Hide Variants": "Nascondi varianti", "Only with PDF": "Solo con PDF", "Optional description for your list": "Descrizione opzionale per la tua lista", "Page Expired": "Pagina scaduta", diff --git a/lang/nb.json b/lang/nb.json index edc7a8f..4659a2e 100644 --- a/lang/nb.json +++ b/lang/nb.json @@ -144,7 +144,7 @@ "Not Found": "Ikke funnet", "Oldest first": "Eldste først", "Only the owner can share this list.": "Bare eieren kan dele denne listen.", - "Show Canonical Recipes": "Vis også oppskriftsvarianter", + "Hide Variants": "Skjul varianter", "Only with PDF": "Kun med PDF", "Optional description for your list": "Valgfri beskrivelse for listen din", "Page Expired": "Siden har utløpt", diff --git a/lang/nl.json b/lang/nl.json index bfcf60d..6bbb903 100644 --- a/lang/nl.json +++ b/lang/nl.json @@ -144,7 +144,7 @@ "Not Found": "Niet gevonden", "Oldest first": "Oudste eerst", "Only the owner can share this list.": "Alleen de eigenaar kan deze lijst delen.", - "Show Canonical Recipes": "Ook receptvarianten tonen", + "Hide Variants": "Varianten verbergen", "Only with PDF": "Alleen met PDF", "Optional description for your list": "Optionele beschrijving voor je lijst", "Page Expired": "Pagina niet meer geldig", diff --git a/lang/sv.json b/lang/sv.json index 60eead6..a7b6e14 100644 --- a/lang/sv.json +++ b/lang/sv.json @@ -144,7 +144,7 @@ "Not Found": "Hittades ej", "Oldest first": "Äldsta först", "Only the owner can share this list.": "Endast ägaren kan dela denna lista.", - "Show Canonical Recipes": "Visa även receptvarianter", + "Hide Variants": "Dölj varianter", "Only with PDF": "Endast med PDF", "Optional description for your list": "Valfri beskrivning för din lista", "Page Expired": "Sidan är utgången", diff --git a/resources/views/web/partials/recipes/filter-modal.blade.php b/resources/views/web/partials/recipes/filter-modal.blade.php index 82a0299..d9a032d 100644 --- a/resources/views/web/partials/recipes/filter-modal.blade.php +++ b/resources/views/web/partials/recipes/filter-modal.blade.php @@ -5,7 +5,7 @@ - + {{ __('Difficulty') }} diff --git a/tests/Feature/FilterShareTest.php b/tests/Feature/FilterShareTest.php index 7a4d227..541e36f 100644 --- a/tests/Feature/FilterShareTest.php +++ b/tests/Feature/FilterShareTest.php @@ -13,6 +13,7 @@ use Livewire\Livewire; use Override; use PHPUnit\Framework\Attributes\Test; +use ReflectionClass; use Tests\TestCase; final class FilterShareTest extends TestCase @@ -133,14 +134,14 @@ public function filter_share_only_includes_active_filters(): void { Livewire::test(RecipeIndex::class) ->set('filterHasPdf', true) - ->set('filterShowCanonical', false) + ->set('filterHideVariants', false) ->set('excludedAllergenIds', []) ->call('generateFilterShareUrl'); $filterShare = FilterShare::latest('created_at')->first(); $this->assertTrue($filterShare->filters['has_pdf']); - $this->assertArrayNotHasKey('show_canonical', $filterShare->filters); + $this->assertArrayNotHasKey('hide_variants', $filterShare->filters); $this->assertArrayNotHasKey('excluded_allergens', $filterShare->filters); } @@ -171,7 +172,7 @@ public function get_share_page_returns_integer(): void { $component = Livewire::test(RecipeIndex::class); - $reflection = new \ReflectionClass($component->instance()); + $reflection = new ReflectionClass($component->instance()); $method = $reflection->getMethod('getSharePage'); $result = $method->invoke($component->instance()); @@ -189,7 +190,7 @@ public function get_share_page_handles_string_page_parameter(): void $mock->method('getPage') ->willReturn('2'); - $reflection = new \ReflectionClass($mock); + $reflection = new ReflectionClass($mock); $method = $reflection->getMethod('getSharePage'); $result = $method->invoke($mock); diff --git a/tests/Feature/Livewire/Recipes/RecipeIndexTest.php b/tests/Feature/Livewire/Recipes/RecipeIndexTest.php index 4d2cc43..bb209a1 100644 --- a/tests/Feature/Livewire/Recipes/RecipeIndexTest.php +++ b/tests/Feature/Livewire/Recipes/RecipeIndexTest.php @@ -445,4 +445,41 @@ public function is_tag_active_returns_false_for_inactive_tag(): void $this->assertFalse($component->instance()->isTagActive($tag->id)); } + + #[Test] + public function it_shows_variants_by_default(): void + { + Recipe::factory()->for($this->country)->create([ + 'name' => ['en' => 'Main Recipe'], + 'variant' => false, + ]); + + Recipe::factory()->for($this->country)->create([ + 'name' => ['en' => 'Variant Recipe'], + 'variant' => true, + ]); + + Livewire::test(RecipeIndex::class) + ->assertSee('Main Recipe') + ->assertSee('Variant Recipe'); + } + + #[Test] + public function it_can_hide_variants(): void + { + Recipe::factory()->for($this->country)->create([ + 'name' => ['en' => 'Main Recipe'], + 'variant' => false, + ]); + + Recipe::factory()->for($this->country)->create([ + 'name' => ['en' => 'Variant Recipe'], + 'variant' => true, + ]); + + Livewire::test(RecipeIndex::class) + ->set('filterHideVariants', true) + ->assertSee('Main Recipe') + ->assertDontSee('Variant Recipe'); + } }