Compare commits

...

1 Commits

Author SHA1 Message Date
400c147a74 feat: CTA dos cards e faixa de modalidades enviam ao WhatsApp com mensagem por pacote
Campo whatsapp_message editável por modalidade (Filament) com fallback para
template padrão. Cards da home, faixa 'Conversar com a Amare' e CTA final
da página de detalhe abrem o WhatsApp diretamente quando número configurado,
mantendo briefing como fallback.
2026-08-12 10:11:06 -03:00
10 changed files with 134 additions and 8 deletions

View File

@@ -67,6 +67,10 @@ class WeddingPackageForm
->label('Texto do botão')
->required()
->maxLength(255),
Textarea::make('whatsapp_message')
->label('Mensagem do WhatsApp (deixe vazio para usar o padrão)')
->helperText('Texto enviado ao cliente ao tocar no botão de contato. O nome da modalidade é citado automaticamente se vazio.')
->rows(3),
TextInput::make('compare_heading')
->label('Comparação — título (leitura rápida)')
->maxLength(255),

View File

@@ -23,6 +23,7 @@ use Illuminate\Support\Str;
* @property string $summary
* @property list<string> $scope_items
* @property string $cta_label
* @property string|null $whatsapp_message
* @property string|null $compare_heading
* @property string|null $compare_summary
* @property string|null $eyebrow
@@ -54,6 +55,7 @@ use Illuminate\Support\Str;
'summary',
'scope_items',
'cta_label',
'whatsapp_message',
'compare_heading',
'compare_summary',
'sort_order',

View File

@@ -11,17 +11,47 @@ final class PackageContactLink
/**
* Resolve the contextual contact link for a wedding package modality.
*
* Uses the package-specific WhatsApp message when provided; otherwise
* falls back to the default template with the modality name.
*
* @return array{href: string, isWhatsapp: bool}
*/
public static function for(SiteSetting $settings, string $packageName): array
public static function for(SiteSetting $settings, string $packageName, ?string $customMessage = null): array
{
$message = filled($customMessage)
? $customMessage
: 'Olá, gostaria de conversar sobre a modalidade '.$packageName.' para meu casamento.';
return self::build($settings, $message, $packageName);
}
/**
* Resolve a generic contact link (no specific modality), used by the
* "Conversar com a Amare" guidance band.
*
* @return array{href: string, isWhatsapp: bool}
*/
public static function generic(SiteSetting $settings): array
{
return self::build(
$settings,
'Olá, ainda estou decidindo a modalidade ideal para o meu casamento. Podemos conversar?',
null,
);
}
/**
* @return array{href: string, isWhatsapp: bool}
*/
private static function build(SiteSetting $settings, string $message, ?string $packageName): array
{
$digits = preg_replace('/\D/', '', (string) $settings->whatsapp_number);
$isWhatsapp = strlen($digits) >= 10;
return [
'href' => $isWhatsapp
? 'https://wa.me/'.$digits.'?text='.rawurlencode('Olá, gostaria de conversar sobre a modalidade '.$packageName.' para meu casamento.')
: route('briefing', ['servico_interesse' => $packageName]),
? 'https://wa.me/'.$digits.'?text='.rawurlencode($message)
: route('briefing', $packageName !== null ? ['servico_interesse' => $packageName] : []),
'isWhatsapp' => $isWhatsapp,
];
}

View File

@@ -0,0 +1,24 @@
<?php
declare(strict_types=1);
use Illuminate\Database\Migrations\Migration;
use Illuminate\Database\Schema\Blueprint;
use Illuminate\Support\Facades\Schema;
return new class extends Migration
{
public function up(): void
{
Schema::table('wedding_packages', function (Blueprint $table): void {
$table->text('whatsapp_message')->nullable()->after('cta_label');
});
}
public function down(): void
{
Schema::table('wedding_packages', function (Blueprint $table): void {
$table->dropColumn('whatsapp_message');
});
}
};

View File

@@ -15,7 +15,9 @@
])
@php
$bandHref = $bandCtaHref ?? route('briefing');
$bandLink = $bandCtaHref
? ['href' => $bandCtaHref, 'isWhatsapp' => false]
: \App\Support\PackageContactLink::generic($settings);
@endphp
<section
aria-labelledby="packages-heading"
@@ -52,7 +54,7 @@
</ul>
</div>
<div class="space-y-5">
@php($contactLink = $ctaRoute ? null : \App\Support\PackageContactLink::for($settings, $package->name))
@php($contactLink = $ctaRoute ? null : \App\Support\PackageContactLink::for($settings, $package->name, $package->whatsapp_message))
<a
href="{{ $ctaRoute ? route($ctaRoute) : $contactLink['href'] }}"
@if ($ctaRoute === null && $contactLink['isWhatsapp']) target="_blank" rel="noopener noreferrer" @endif
@@ -77,7 +79,11 @@
<h3 class="text-[clamp(1.5625rem,2.7vw,2.125rem)] font-medium leading-tight">Ainda não sabe qual modalidade é ideal?</h3>
<p class="text-amare-accent-text/80">{{ $bandBody }}</p>
</div>
<a href="{{ $bandHref }}" class="inline-flex min-h-[48px] items-center justify-center self-start border border-amare-accent-text px-6 text-xs font-bold uppercase tracking-[0.09em] text-amare-accent-text transition-colors hover:bg-amare-accent-text hover:text-amare-accent-deep md:self-auto">
<a
href="{{ $bandLink['href'] }}"
@if ($bandLink['isWhatsapp']) target="_blank" rel="noopener noreferrer" @endif
class="inline-flex min-h-[48px] items-center justify-center self-start border border-amare-accent-text px-6 text-xs font-bold uppercase tracking-[0.09em] text-amare-accent-text transition-colors hover:bg-amare-accent-text hover:text-amare-accent-deep md:self-auto"
>
Conversar com a Amare
</a>
</div>

View File

@@ -8,7 +8,7 @@
@else
<ol class="grid gap-8 border-t border-amare-border pt-6 md:grid-cols-3">
@foreach ($packages as $package)
@php($contactLink = \App\Support\PackageContactLink::for($settings, $package->name))
@php($contactLink = \App\Support\PackageContactLink::for($settings, $package->name, $package->whatsapp_message))
<li class="space-y-5 border-t border-amare-border pt-4" data-reveal data-reveal-from="up"><p class="text-xs font-semibold uppercase tracking-[.14em] text-amare-accent">{{ $package->level }}</p><h3 class="text-3xl font-medium">{{ $package->name }}</h3><p class="text-amare-muted">{{ $package->summary }}</p><ul class="space-y-2 text-sm text-amare-text-muted">@foreach ($package->scope_items as $item)<li class="border-l border-amare-border pl-3">{{ $item }}</li>@endforeach</ul><div class="space-y-2"><a href="{{ $contactLink['href'] }}" @if ($contactLink['isWhatsapp']) target="_blank" rel="noopener" @endif class="btn btn-ghost min-h-0 px-0 text-sm font-semibold text-amare-accent hover:bg-transparent hover:text-amare-accent-deep"><span class="border-b border-amare-accent pb-1">{{ $package->cta_label }}</span></a><a href="{{ route('packages.show', $package->slug) }}" class="btn btn-ghost min-h-0 px-0 text-sm font-semibold text-amare-accent hover:bg-transparent hover:text-amare-accent-deep"><span class="border-b border-amare-accent pb-1">Conhecer esta modalidade</span></a></div></li>
@endforeach
</ol>

View File

@@ -3,10 +3,11 @@
'heading' => null,
'body' => null,
'packageName' => null,
'packageMessage' => null,
])
@php
$link = \App\Support\PackageContactLink::for($settings, $packageName ?? '');
$link = \App\Support\PackageContactLink::for($settings, $packageName ?? '', $packageMessage);
@endphp
<section aria-labelledby="final-cta-heading" class="border-b border-amare-border bg-amare-bg py-20 md:py-24" data-chapter="final-cta" data-reveal-group>

View File

@@ -31,5 +31,6 @@
:heading="$package->final_cta_heading"
:body="$package->final_cta_body"
:package-name="$package->name"
:package-message="$package->whatsapp_message"
/>
@endsection

View File

@@ -216,6 +216,44 @@ class HomePageContentTest extends TestCase
->assertDontSee(route('briefing', ['servico_interesse' => 'Grand Jour']), false);
}
public function test_package_cta_uses_custom_whatsapp_message_when_provided(): void
{
SiteSetting::instance()->update(['whatsapp_number' => '+55 11 98888-7777']);
WeddingPackage::factory()->published()->create([
'name' => 'Grand Jour',
'cta_label' => 'Quero conhecer a Grand Jour',
'whatsapp_message' => 'Olá, quero saber mais sobre a modalidade Grand Jour para o meu casamento em julho.',
]);
$response = $this->get(route('home'));
$response
->assertOk()
->assertSee(
'https://wa.me/5511988887777?text='.rawurlencode('Olá, quero saber mais sobre a modalidade Grand Jour para o meu casamento em julho.'),
false,
)
->assertDontSee('gostaria de conversar sobre a modalidade Grand Jour para meu casamento', false);
}
public function test_guidance_band_uses_whatsapp_when_a_number_is_configured(): void
{
SiteSetting::instance()->update(['whatsapp_number' => '+55 11 98888-7777']);
WeddingPackage::factory()->published()->create();
$response = $this->get(route('home'));
$response
->assertOk()
->assertSee(
'https://wa.me/5511988887777?text='.rawurlencode('Olá, ainda estou decidindo a modalidade ideal para o meu casamento. Podemos conversar?'),
false,
)
->assertSee('Conversar com a Amare');
}
public function test_corporate_steps_and_placeholder_render_from_settings(): void
{
SiteSetting::instance()->update([

View File

@@ -100,6 +100,26 @@ class PackageDetailTest extends TestCase
->assertDontSee(route('briefing', ['servico_interesse' => 'Essenza']), false);
}
public function test_package_final_cta_uses_custom_whatsapp_message_when_provided(): void
{
SiteSetting::instance()->update(['whatsapp_number' => '+55 11 98888-7777']);
$package = WeddingPackage::factory()->published()->create([
'name' => 'Essenza',
'whatsapp_message' => 'Olá, tenho interesse na assessoria Essenza. Podem me passar mais detalhes?',
]);
$response = $this->get(route('packages.show', $package->slug));
$response
->assertOk()
->assertSee(
'https://wa.me/5511988887777?text='.rawurlencode('Olá, tenho interesse na assessoria Essenza. Podem me passar mais detalhes?'),
false,
)
->assertDontSee('gostaria de conversar sobre a modalidade Essenza para meu casamento', false);
}
public function test_package_cta_falls_back_to_briefing_without_whatsapp_number(): void
{
SiteSetting::instance();