Compare commits
4 Commits
f4c2e4ed2f
...
feat/man-1
| Author | SHA1 | Date | |
|---|---|---|---|
| 89df85eb1f | |||
| 39a7a310e9 | |||
| 93b680c172 | |||
| ba388942c0 |
19
.gitea/PULL_REQUEST_TEMPLATE.md
Normal file
19
.gitea/PULL_REQUEST_TEMPLATE.md
Normal file
@@ -0,0 +1,19 @@
|
||||
## WHAT
|
||||
|
||||
<!-- What changed? -->
|
||||
|
||||
## WHY
|
||||
|
||||
<!-- Why this change? -->
|
||||
|
||||
## HOW
|
||||
|
||||
<!-- How was it implemented? Include verification. -->
|
||||
|
||||
## Linear Issue
|
||||
|
||||
<!-- Required. Identifier + URL, e.g. MAN-133 https://linear.app/maneco-workspace/issue/MAN-133 -->
|
||||
|
||||
## Comments
|
||||
|
||||
<!-- Notes for reviewers. Screenshots, follow-ups, out of scope. -->
|
||||
19
.github/pull_request_template.md
vendored
Normal file
19
.github/pull_request_template.md
vendored
Normal file
@@ -0,0 +1,19 @@
|
||||
## WHAT
|
||||
|
||||
<!-- What changed? -->
|
||||
|
||||
## WHY
|
||||
|
||||
<!-- Why this change? -->
|
||||
|
||||
## HOW
|
||||
|
||||
<!-- How was it implemented? Include verification. -->
|
||||
|
||||
## Linear Issue
|
||||
|
||||
<!-- Required. Identifier + URL, e.g. MAN-133 https://linear.app/maneco-workspace/issue/MAN-133 -->
|
||||
|
||||
## Comments
|
||||
|
||||
<!-- Notes for reviewers. Screenshots, follow-ups, out of scope. -->
|
||||
18
AGENTS.md
18
AGENTS.md
@@ -18,7 +18,19 @@ Feature and browser tests require the `amare_test` PostgreSQL database configure
|
||||
|
||||
## Worktrees
|
||||
|
||||
Always work in a git worktree created from the `main` ref — never modify `main` directly and never commit from the primary working tree. Create a dedicated worktree per feature/branch with `git worktree add -b <branch> <path> main`. On finishing work, create a PR, watch CI until green, then merge it. Clean up the worktree with `git worktree remove` after merge.
|
||||
Always work in a git worktree created from the `main` ref — never modify `main` directly and never commit from the primary working tree. Create a dedicated worktree per feature/branch with `git worktree add -b <branch> <path> main`. Include the Linear identifier in the branch name (e.g. `docs/man-132-…`). When the SDLC gate below is green, commit and open the PR without asking. Watch CI until green, then merge. Clean up the worktree with `git worktree remove` after merge.
|
||||
|
||||
## Agent SDLC (do not ask)
|
||||
|
||||
Never ask whether to commit or open a PR. After each complete slice of work, ship it:
|
||||
|
||||
1. **Work is done** in a Linear-linked worktree.
|
||||
2. **Tests are written or updated** for the changed layer. Skip new tests only when the change has no runtime impact (docs, templates, static config).
|
||||
3. **Pre-commit passes** (`composer pint:check` and `composer phpstan`). Do not `--no-verify`. If the hook fails, fix and rerun.
|
||||
4. **Commit** with Conventional Commits. Cite the Linear identifier.
|
||||
5. **Push and open the Gitea PR** (`tea pulls create`) using `.gitea/PULL_REQUEST_TEMPLATE.md` (WHAT / WHY / HOW / Linear Issue / Comments). Attach the PR URL on the Linear issue via `save_issue` `links`. GitHub is the legacy mirror only.
|
||||
|
||||
Do not wait for "pode commitar?" or "abre o PR?".
|
||||
|
||||
## Git Hooks (husky)
|
||||
|
||||
@@ -41,7 +53,7 @@ Tests use Pest 4; browser coverage uses Pest Browser/Playwright. Tests are verif
|
||||
|
||||
## Commit & Pull Request Guidelines
|
||||
|
||||
History follows Conventional Commit-style subjects, for example `feat: Fase 0 — Fundação`. Use `<type>: <imperative summary>` (`feat`, `fix`, `docs`, `test`, `chore`) and keep commits focused. Pull requests should explain scope, link the relevant issue or OpenSpec requirement, list verification commands, and include screenshots for UI changes. Ensure all CI jobs pass.
|
||||
History follows Conventional Commit-style subjects, for example `feat: Fase 0 — Fundação`. Use `<type>: <imperative summary>` (`feat`, `fix`, `docs`, `test`, `chore`) and keep commits focused. Fill the PR template (WHAT / WHY / HOW / Linear Issue / Comments). **Linear Issue is required** (identifier + URL). An OpenSpec change is extra context, not a substitute. Include verification commands and screenshots for UI changes. Ensure all CI jobs pass. After pre-commit is green, commit and open the PR — do not ask.
|
||||
|
||||
## Security & Configuration
|
||||
|
||||
@@ -59,6 +71,8 @@ The design system lives in `DESIGN.md` (palette, typography, layout, do's and do
|
||||
|
||||
Issues live in Linear, driven through the Linear MCP tools. See `docs/agents/issue-tracker.md` for workspace, team, and tool conventions. The repo ships no `.mcp.json`, so the Linear MCP has to be enabled for the session before those tools exist — if it isn't, report that instead of silently falling back to another tracker.
|
||||
|
||||
**Every piece of work and every PR must be tied to a Linear issue.** Do not create a branch, worktree, or PR until an issue exists (identifier like `MAN-132`). If the user did not give one, search Linear first; if none fits, create it with `save_issue` on team `Maneco-workspace` before starting. Put the identifier in the branch name. Cite identifier + URL in the PR body. After opening the PR, attach the PR URL on the issue via `save_issue` `links`. Do not start untracked work.
|
||||
|
||||
### Triage labels
|
||||
|
||||
Default vocabulary: `needs-triage`, `needs-info`, `ready-for-agent`, `ready-for-human`, `wontfix`. See `docs/agents/triage-labels.md`.
|
||||
|
||||
@@ -4,7 +4,10 @@ declare(strict_types=1);
|
||||
|
||||
namespace App\Application\Queries\Marketing;
|
||||
|
||||
use App\Domain\Marketing\PortfolioVertical;
|
||||
use App\Models\PortfolioCase;
|
||||
use Illuminate\Contracts\Pagination\LengthAwarePaginator;
|
||||
use Illuminate\Database\Eloquent\Builder;
|
||||
use Illuminate\Database\Eloquent\Collection;
|
||||
|
||||
final class GetPublishedPortfolioCases
|
||||
@@ -12,12 +15,37 @@ final class GetPublishedPortfolioCases
|
||||
/**
|
||||
* @return Collection<int, PortfolioCase>
|
||||
*/
|
||||
public function __invoke(): Collection
|
||||
public function __invoke(?PortfolioVertical $vertical = null): Collection
|
||||
{
|
||||
return PortfolioCase::query()
|
||||
return $this->baseQuery($vertical)->get();
|
||||
}
|
||||
|
||||
/**
|
||||
* @return LengthAwarePaginator<int, PortfolioCase>
|
||||
*/
|
||||
public function paginate(?PortfolioVertical $vertical = null, int $perPage = 9): LengthAwarePaginator
|
||||
{
|
||||
return $this->baseQuery($vertical)->paginate($perPage)->withQueryString();
|
||||
}
|
||||
|
||||
/**
|
||||
* @return Builder<PortfolioCase>
|
||||
*/
|
||||
private function baseQuery(?PortfolioVertical $vertical): Builder
|
||||
{
|
||||
$query = PortfolioCase::query()
|
||||
->published()
|
||||
->with(['images'])
|
||||
->orderBy('sort_order')
|
||||
->get();
|
||||
->orderBy('sort_order');
|
||||
|
||||
if ($vertical !== null) {
|
||||
$query->where(function (Builder $builder) use ($vertical): void {
|
||||
foreach ($vertical->eventTypePatterns() as $pattern) {
|
||||
$builder->orWhere('event_type', 'ilike', $pattern);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
return $query;
|
||||
}
|
||||
}
|
||||
|
||||
45
app/Domain/Marketing/PortfolioVertical.php
Normal file
45
app/Domain/Marketing/PortfolioVertical.php
Normal file
@@ -0,0 +1,45 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace App\Domain\Marketing;
|
||||
|
||||
/**
|
||||
* Public portfolio verticals used to separate Casamentos and Corporate proof.
|
||||
*
|
||||
* Matching is intentionally tolerant of free-text `event_type` values already
|
||||
* stored in the CMS (e.g. "Casamento", "Mini wedding", "Corporativo").
|
||||
*/
|
||||
enum PortfolioVertical: string
|
||||
{
|
||||
case Casamentos = 'casamentos';
|
||||
case Corporate = 'corporate';
|
||||
|
||||
public function label(): string
|
||||
{
|
||||
return match ($this) {
|
||||
self::Casamentos => 'Casamentos',
|
||||
self::Corporate => 'Corporate',
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* @return list<string>
|
||||
*/
|
||||
public function eventTypePatterns(): array
|
||||
{
|
||||
return match ($this) {
|
||||
self::Casamentos => ['%casamento%', '%wedding%', '%social%'],
|
||||
self::Corporate => ['%corporat%', '%empresa%', '%business%'],
|
||||
};
|
||||
}
|
||||
|
||||
public static function tryFromQuery(?string $value): ?self
|
||||
{
|
||||
if ($value === null || $value === '') {
|
||||
return null;
|
||||
}
|
||||
|
||||
return self::tryFrom($value);
|
||||
}
|
||||
}
|
||||
@@ -6,33 +6,35 @@ namespace App\Http\Controllers\PublicSite;
|
||||
|
||||
use App\Application\Data\PageMeta;
|
||||
use App\Application\Queries\Marketing\FindPublishedPortfolioCaseBySlug;
|
||||
use App\Application\Queries\Marketing\GetPublishedPortfolioCases;
|
||||
use App\Domain\Marketing\PortfolioVertical;
|
||||
use App\Http\Controllers\Controller;
|
||||
use App\Models\PortfolioCase;
|
||||
use App\Models\SiteSetting;
|
||||
use Illuminate\Contracts\View\View;
|
||||
use Illuminate\Http\Request;
|
||||
use Illuminate\Http\Response;
|
||||
use Illuminate\Pagination\LengthAwarePaginator;
|
||||
|
||||
final class PortfolioController extends Controller
|
||||
{
|
||||
public function index(): View
|
||||
public function index(Request $request, GetPublishedPortfolioCases $getPublishedPortfolioCases): View
|
||||
{
|
||||
$settings = SiteSetting::instance();
|
||||
|
||||
/** @var LengthAwarePaginator<int, PortfolioCase> $cases */
|
||||
$cases = PortfolioCase::query()
|
||||
->published()
|
||||
->with(['images'])
|
||||
->orderBy('sort_order')
|
||||
->paginate(9);
|
||||
$vertical = PortfolioVertical::tryFromQuery($request->query('vertente'));
|
||||
$cases = $getPublishedPortfolioCases->paginate($vertical);
|
||||
|
||||
return view('pages.portfolio.index', [
|
||||
'cases' => $cases,
|
||||
'vertical' => $vertical,
|
||||
'siteSettings' => $settings,
|
||||
'pageMeta' => PageMeta::forPage(
|
||||
canonical: route('portfolio.index'),
|
||||
canonical: route('portfolio.index', array_filter([
|
||||
'vertente' => $vertical?->value,
|
||||
])),
|
||||
settings: $settings,
|
||||
title: PageMeta::withBrandSuffix('Portfólio', $settings),
|
||||
title: PageMeta::withBrandSuffix(
|
||||
$vertical === null ? 'Portfólio' : 'Portfólio — '.$vertical->label(),
|
||||
$settings,
|
||||
),
|
||||
description: 'Casos reais de eventos conduzidos pela '.$settings->brand_name.'.',
|
||||
),
|
||||
]);
|
||||
|
||||
@@ -17,6 +17,16 @@ Issues and specs for this repo live in Linear. Use the Linear MCP tools for all
|
||||
- **Link work**: `save_issue` with `project`, `cycle`, `parentId`, `blocks` / `blockedBy` (relations are append-only).
|
||||
- **Cross-link to a PR**: attach the PR URL via `save_issue` `links`.
|
||||
|
||||
## Required: issue before work and before PR
|
||||
|
||||
Every branch, worktree, and pull request in this repo must be tied to a Linear issue.
|
||||
|
||||
1. **Starting work:** require an identifier (e.g. `MAN-132`). If the user did not give one, search Linear. If none fits, create the issue with `save_issue` on team `Maneco-workspace` *before* creating a branch or worktree.
|
||||
2. **Branch:** include the identifier (`docs/man-132-…`, `feat/man-126-…`).
|
||||
3. **PR:** cite identifier + URL in the body. An OpenSpec change is extra context, not a substitute.
|
||||
4. **After the PR exists:** attach the PR URL on the issue via `save_issue` `links`. Do this as part of the automatic SDLC in `AGENTS.md` — do not ask before opening the PR.
|
||||
5. If Linear MCP is unavailable, stop and report — do not start untracked work.
|
||||
|
||||
## When a skill says "publish to the issue tracker"
|
||||
|
||||
Create a Linear issue with `save_issue` on team `Maneco-workspace`.
|
||||
|
||||
@@ -134,7 +134,8 @@
|
||||
overflow-wrap: break-word;
|
||||
}
|
||||
|
||||
[id$='-heading'] {
|
||||
[id$='-heading'],
|
||||
.home-chapter[id] {
|
||||
scroll-margin-top: 5.5rem;
|
||||
}
|
||||
|
||||
|
||||
@@ -58,8 +58,8 @@
|
||||
<header class="site-header sticky top-0 z-40 border-b border-amare-border bg-amare-bg/95 backdrop-blur-sm">
|
||||
<div class="container-amare grid grid-cols-[auto_1fr_auto] items-center gap-4 py-4 md:grid-cols-[1fr_auto_1fr] md:h-[74px] md:py-0">
|
||||
<nav id="main-nav" class="main-nav order-3 col-span-3 hidden flex-col gap-4 border-t border-amare-border pt-4 md:order-1 md:col-span-1 md:flex md:flex-row md:items-center md:gap-1 md:border-0 md:pt-0" aria-label="Principal" data-main-nav>
|
||||
<a href="{{ route('home') }}" class="btn btn-ghost min-h-0 px-0 py-3 text-[13px] font-semibold uppercase tracking-[0.08em] text-amare-muted hover:text-amare-accent md:px-3">Início</a>
|
||||
<a href="{{ route('services.index') }}" class="btn btn-ghost min-h-0 px-0 py-3 text-[13px] font-semibold uppercase tracking-[0.08em] text-amare-muted hover:text-amare-accent md:px-3">Serviços</a>
|
||||
<a href="{{ url('/#casamentos') }}" class="btn btn-ghost min-h-0 px-0 py-3 text-[13px] font-semibold uppercase tracking-[0.08em] text-amare-muted hover:text-amare-accent md:px-3">Casamentos</a>
|
||||
<a href="{{ url('/#corporate') }}" class="btn btn-ghost min-h-0 px-0 py-3 text-[13px] font-semibold uppercase tracking-[0.08em] text-amare-muted hover:text-amare-accent md:px-3">Corporate</a>
|
||||
<a href="{{ route('portfolio.index') }}" class="btn btn-ghost min-h-0 px-0 py-3 text-[13px] font-semibold uppercase tracking-[0.08em] text-amare-muted hover:text-amare-accent md:px-3">Portfólio</a>
|
||||
<a href="{{ route('about') }}" class="btn btn-ghost min-h-0 px-0 py-3 text-[13px] font-semibold uppercase tracking-[0.08em] text-amare-muted hover:text-amare-accent md:px-3">Amare</a>
|
||||
<a href="{{ route('briefing') }}" class="btn btn-ghost min-h-0 px-0 py-3 text-[13px] font-semibold uppercase tracking-[0.08em] text-amare-muted hover:text-amare-accent md:hidden">Solicitar proposta</a>
|
||||
@@ -73,7 +73,6 @@
|
||||
<a href="{{ route('briefing') }}" class="btn btn-outline hidden min-h-0 px-6 text-xs font-bold uppercase tracking-[0.09em] md:inline-flex">
|
||||
Conte seu evento
|
||||
</a>
|
||||
</a>
|
||||
|
||||
<button
|
||||
type="button"
|
||||
@@ -111,10 +110,11 @@
|
||||
$footerSocials = collect($siteSettings->social_links ?? [])->filter(static fn ($url) => filled($url));
|
||||
@endphp
|
||||
|
||||
<div class="container-amare flex flex-col gap-8 py-14 md:flex-row md:items-end md:justify-between md:py-[58px]">
|
||||
<div class="container-amare flex flex-col gap-10 py-14 md:py-[58px]">
|
||||
<div class="flex flex-col gap-8 md:flex-row md:items-end md:justify-between">
|
||||
<div class="space-y-3">
|
||||
<x-brand.logo variant="on-light" class="h-10 w-auto" />
|
||||
<p class="text-sm text-amare-muted">Assessoria & produção de eventos • São Paulo</p>
|
||||
<p class="text-sm text-amare-muted">Boutique de assessoria & produção • São Paulo</p>
|
||||
</div>
|
||||
|
||||
<p class="text-sm text-amare-muted md:text-right">
|
||||
@@ -128,12 +128,21 @@
|
||||
</p>
|
||||
</div>
|
||||
|
||||
<nav aria-label="Rodapé" class="flex flex-col gap-4 border-t border-amare-border pt-8 text-sm text-amare-muted md:flex-row md:flex-wrap md:items-center md:gap-x-6 md:gap-y-2">
|
||||
<a href="{{ url('/#casamentos') }}" class="inline-flex min-h-11 items-center transition-colors hover:text-amare-accent">Casamentos</a>
|
||||
<a href="{{ url('/#corporate') }}" class="inline-flex min-h-11 items-center transition-colors hover:text-amare-accent">Corporate</a>
|
||||
<a href="{{ route('services.index') }}" class="inline-flex min-h-11 items-center transition-colors hover:text-amare-accent">Serviços</a>
|
||||
<a href="{{ route('portfolio.index') }}" class="inline-flex min-h-11 items-center transition-colors hover:text-amare-accent">Portfólio</a>
|
||||
<a href="{{ route('about') }}" class="inline-flex min-h-11 items-center transition-colors hover:text-amare-accent">Sobre</a>
|
||||
<a href="{{ route('briefing') }}" class="inline-flex min-h-11 items-center transition-colors hover:text-amare-accent">Briefing</a>
|
||||
<a href="{{ route('contact') }}" class="inline-flex min-h-11 items-center transition-colors hover:text-amare-accent">Parcerias</a>
|
||||
<a href="{{ route('privacy') }}" class="inline-flex min-h-11 items-center transition-colors hover:text-amare-accent">Privacidade</a>
|
||||
</nav>
|
||||
</div>
|
||||
|
||||
<div class="border-t border-amare-border">
|
||||
<div class="container-amare flex flex-col gap-2 py-6 text-sm text-amare-muted md:flex-row md:items-center md:justify-between">
|
||||
<p>© {{ now()->year }} {{ $siteSettings->brand_name }}. Todos os direitos reservados.</p>
|
||||
<p>
|
||||
<a href="{{ route('privacy') }}" class="inline-flex min-h-11 items-center transition-colors hover:text-amare-accent">Política de privacidade</a>
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
</footer>
|
||||
|
||||
@@ -5,9 +5,38 @@
|
||||
|
||||
<div class="border-b border-amare-border bg-amare-bg-deep">
|
||||
<div class="container-amare space-y-10 py-16 md:py-24">
|
||||
<nav aria-label="Vertentes do portfólio" class="flex flex-wrap gap-x-6 gap-y-2 border-b border-amare-border pb-6 text-sm">
|
||||
<a
|
||||
href="{{ route('portfolio.index') }}"
|
||||
@class([
|
||||
'inline-flex min-h-11 items-center uppercase tracking-[0.08em] transition-colors',
|
||||
'font-semibold text-amare-accent' => $vertical === null,
|
||||
'text-amare-muted hover:text-amare-accent' => $vertical !== null,
|
||||
])
|
||||
>Todos</a>
|
||||
@foreach (\App\Domain\Marketing\PortfolioVertical::cases() as $option)
|
||||
<a
|
||||
href="{{ route('portfolio.index', ['vertente' => $option->value]) }}"
|
||||
@class([
|
||||
'inline-flex min-h-11 items-center uppercase tracking-[0.08em] transition-colors',
|
||||
'font-semibold text-amare-accent' => $vertical === $option,
|
||||
'text-amare-muted hover:text-amare-accent' => $vertical !== $option,
|
||||
])
|
||||
>{{ $option->label() }}</a>
|
||||
@endforeach
|
||||
</nav>
|
||||
|
||||
@if ($cases->isEmpty())
|
||||
@if ($vertical === \App\Domain\Marketing\PortfolioVertical::Corporate)
|
||||
<div class="flex min-h-[280px] flex-col items-center justify-center gap-4 bg-amare-bg p-10 text-center">
|
||||
<p class="text-xs font-bold uppercase tracking-[0.16em] text-amare-accent-deep">Portfólio Corporate</p>
|
||||
<h2 class="text-[clamp(1.5625rem,2.7vw,2.125rem)] font-medium leading-tight text-amare-text">Conteúdo em construção</h2>
|
||||
<p class="max-w-lg text-amare-text-muted">Ainda não publicamos cases corporativos autorizados. Em vez de inventar prova, mantemos este espaço pronto para projetos reais.</p>
|
||||
<a href="{{ route('briefing') }}" class="btn btn-outline mt-2 text-xs font-bold uppercase tracking-[0.09em]">Falar sobre um evento corporativo</a>
|
||||
</div>
|
||||
@else
|
||||
<p class="text-amare-muted">Novos casos serão publicados assim que o acervo estiver organizado. Enquanto isso, fale conosco para conhecer o nosso trabalho.</p>
|
||||
@endif
|
||||
@else
|
||||
@php
|
||||
$hasPrioritizedImage = false;
|
||||
@@ -53,6 +82,9 @@
|
||||
@endphp
|
||||
@endif
|
||||
<div class="space-y-2 border-t border-amare-border pt-4">
|
||||
@if (filled($case->event_type))
|
||||
<p class="text-xs font-bold uppercase tracking-[0.16em] text-amare-accent-deep">{{ $case->event_type }}</p>
|
||||
@endif
|
||||
<h2 class="text-2xl font-medium text-amare-text">
|
||||
<a href="{{ route('portfolio.show', $case->slug) }}" class="transition-colors hover:text-amare-accent">{{ $case->title }}</a>
|
||||
</h2>
|
||||
|
||||
@@ -5,6 +5,7 @@ declare(strict_types=1);
|
||||
namespace Tests\Feature\Application\Queries\Marketing;
|
||||
|
||||
use App\Application\Queries\Marketing\GetPublishedPortfolioCases;
|
||||
use App\Domain\Marketing\PortfolioVertical;
|
||||
use App\Models\PortfolioCase;
|
||||
use App\Models\PortfolioImage;
|
||||
use Illuminate\Foundation\Testing\RefreshDatabase;
|
||||
@@ -58,4 +59,26 @@ class GetPublishedPortfolioCasesTest extends TestCase
|
||||
$this->assertTrue($cases->get(0)?->relationLoaded('images'));
|
||||
$this->assertCount(2, $cases->get(0)?->images ?? []);
|
||||
}
|
||||
|
||||
public function test_filters_published_cases_by_portfolio_vertical(): void
|
||||
{
|
||||
PortfolioCase::factory()->published()->create([
|
||||
'title' => 'Wedding Case',
|
||||
'event_type' => 'Mini wedding',
|
||||
'sort_order' => 10,
|
||||
]);
|
||||
PortfolioCase::factory()->published()->create([
|
||||
'title' => 'Corporate Case',
|
||||
'event_type' => 'Evento corporativo',
|
||||
'sort_order' => 20,
|
||||
]);
|
||||
|
||||
$weddings = (new GetPublishedPortfolioCases)(PortfolioVertical::Casamentos);
|
||||
$corporate = (new GetPublishedPortfolioCases)(PortfolioVertical::Corporate);
|
||||
|
||||
$this->assertCount(1, $weddings);
|
||||
$this->assertSame('Wedding Case', $weddings->first()?->title);
|
||||
$this->assertCount(1, $corporate);
|
||||
$this->assertSame('Corporate Case', $corporate->first()?->title);
|
||||
}
|
||||
}
|
||||
|
||||
60
tests/Feature/PublicSite/PortfolioVerticalFilterTest.php
Normal file
60
tests/Feature/PublicSite/PortfolioVerticalFilterTest.php
Normal file
@@ -0,0 +1,60 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace Tests\Feature\PublicSite;
|
||||
|
||||
use App\Models\PortfolioCase;
|
||||
use App\Models\SiteSetting;
|
||||
use Illuminate\Foundation\Testing\RefreshDatabase;
|
||||
use Tests\TestCase;
|
||||
|
||||
class PortfolioVerticalFilterTest extends TestCase
|
||||
{
|
||||
use RefreshDatabase;
|
||||
|
||||
public function test_portfolio_index_filters_by_vertical_and_shows_corporate_empty_state(): void
|
||||
{
|
||||
SiteSetting::instance();
|
||||
|
||||
PortfolioCase::factory()->published()->create([
|
||||
'title' => 'Casamento Jardim',
|
||||
'slug' => 'casamento-jardim',
|
||||
'event_type' => 'Casamento',
|
||||
'sort_order' => 10,
|
||||
]);
|
||||
|
||||
PortfolioCase::factory()->published()->create([
|
||||
'title' => 'Convenção Anual',
|
||||
'slug' => 'convencao-anual',
|
||||
'event_type' => 'Corporativo',
|
||||
'sort_order' => 20,
|
||||
]);
|
||||
|
||||
$this->get(route('portfolio.index'))
|
||||
->assertOk()
|
||||
->assertSee('Casamento Jardim')
|
||||
->assertSee('Convenção Anual')
|
||||
->assertSee('Vertentes do portfólio')
|
||||
->assertSee('Casamentos')
|
||||
->assertSee('Corporate');
|
||||
|
||||
$this->get(route('portfolio.index', ['vertente' => 'casamentos']))
|
||||
->assertOk()
|
||||
->assertSee('Casamento Jardim')
|
||||
->assertDontSee('Convenção Anual');
|
||||
|
||||
$this->get(route('portfolio.index', ['vertente' => 'corporate']))
|
||||
->assertOk()
|
||||
->assertSee('Convenção Anual')
|
||||
->assertDontSee('Casamento Jardim');
|
||||
|
||||
PortfolioCase::query()->where('slug', 'convencao-anual')->update(['published_at' => null]);
|
||||
|
||||
$this->get(route('portfolio.index', ['vertente' => 'corporate']))
|
||||
->assertOk()
|
||||
->assertSee('Portfólio Corporate')
|
||||
->assertSee('Conteúdo em construção')
|
||||
->assertDontSee('Convenção Anual');
|
||||
}
|
||||
}
|
||||
@@ -54,7 +54,8 @@ class PublicLayoutSeoTest extends TestCase
|
||||
->assertSee('Amare Brand')
|
||||
->assertSee('hello@amare.test')
|
||||
->assertSee('(11) 91111-1111')
|
||||
->assertSee('Política de privacidade')
|
||||
->assertSee('Privacidade')
|
||||
->assertSee(route('privacy'), false)
|
||||
->assertSee('https://instagram.com/amare', false);
|
||||
}
|
||||
}
|
||||
|
||||
43
tests/Feature/PublicSite/PublicNavigationVerticalsTest.php
Normal file
43
tests/Feature/PublicSite/PublicNavigationVerticalsTest.php
Normal file
@@ -0,0 +1,43 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace Tests\Feature\PublicSite;
|
||||
|
||||
use App\Models\SiteSetting;
|
||||
use Illuminate\Foundation\Testing\RefreshDatabase;
|
||||
use Tests\TestCase;
|
||||
|
||||
class PublicNavigationVerticalsTest extends TestCase
|
||||
{
|
||||
use RefreshDatabase;
|
||||
|
||||
public function test_header_and_footer_expose_casamentos_and_corporate_without_implying_weddings_only(): void
|
||||
{
|
||||
SiteSetting::instance();
|
||||
|
||||
$response = $this->get(route('about'))->assertOk();
|
||||
|
||||
$homeCasamentos = url('/#casamentos');
|
||||
$homeCorporate = url('/#corporate');
|
||||
|
||||
$response
|
||||
->assertSee('id="main-nav"', false)
|
||||
->assertSee('href="'.$homeCasamentos.'"', false)
|
||||
->assertSee('>Casamentos</a>', false)
|
||||
->assertSee('href="'.$homeCorporate.'"', false)
|
||||
->assertSee('>Corporate</a>', false)
|
||||
->assertSee('href="'.route('portfolio.index').'"', false)
|
||||
->assertSee('href="'.route('about').'"', false)
|
||||
->assertSee('href="'.route('services.index').'"', false)
|
||||
->assertSee('href="'.route('privacy').'"', false)
|
||||
->assertSee('aria-label="Rodapé"', false)
|
||||
->assertSeeInOrder([
|
||||
'aria-label="Rodapé"',
|
||||
'href="'.$homeCasamentos.'"',
|
||||
'Casamentos',
|
||||
'href="'.$homeCorporate.'"',
|
||||
'Corporate',
|
||||
], false);
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user