feat: Home editorial conforme preview (header, footer, 8 seções) (#53)

* feat: modalidades de casamento e etapas corporativas

* feat: home editorial e header/footer conforme preview

* docs: proposta openspec home editorial

* test: CTA hero agora ancora #sobre conforme preview

* test: speed browser suite via visit reuse

Collapse route×viewport cartesian products, merge home motion
asserts, skip existing VisualContentSeeder JPEGs, ignore Screenshots.
This commit is contained in:
2026-08-11 18:14:24 -03:00
committed by GitHub
parent d0e508b237
commit 484ac77ce7
38 changed files with 1312 additions and 595 deletions

View File

@@ -5,15 +5,9 @@ declare(strict_types=1);
namespace App\Filament\Resources\WeddingPackages\Pages;
use App\Filament\Resources\WeddingPackages\WeddingPackageResource;
use Filament\Actions\DeleteAction;
use Filament\Resources\Pages\EditRecord;
class EditWeddingPackage extends EditRecord
{
protected static string $resource = WeddingPackageResource::class;
protected function getHeaderActions(): array
{
return [DeleteAction::make()];
}
}

View File

@@ -14,6 +14,8 @@ class ListWeddingPackages extends ListRecords
protected function getHeaderActions(): array
{
return [CreateAction::make()];
return [
CreateAction::make(),
];
}
}

View File

@@ -0,0 +1,57 @@
<?php
declare(strict_types=1);
namespace App\Filament\Resources\WeddingPackages\Schemas;
use Filament\Forms\Components\DateTimePicker;
use Filament\Forms\Components\Repeater;
use Filament\Forms\Components\Textarea;
use Filament\Forms\Components\TextInput;
use Filament\Schemas\Schema;
class WeddingPackageForm
{
public static function configure(Schema $schema): Schema
{
return $schema
->components([
TextInput::make('name')
->label('Nome da modalidade')
->required()
->maxLength(255),
TextInput::make('level')
->label('Número / tipo (ex.: 01 / COMPLETA)')
->required()
->maxLength(255),
Textarea::make('summary')
->label('Resumo')
->required()
->rows(4),
Repeater::make('scope_items')
->label('O que está incluído')
->schema([
TextInput::make('item')
->label('Item')
->required()
->maxLength(255),
])
->defaultItems(4)
->minItems(1)
->addActionLabel('Adicionar item')
->required(),
TextInput::make('cta_label')
->label('Texto do botão')
->required()
->maxLength(255),
TextInput::make('sort_order')
->label('Ordem')
->numeric()
->default(0)
->required(),
DateTimePicker::make('published_at')
->label('Publicado em')
->seconds(false),
]);
}
}

View File

@@ -0,0 +1,50 @@
<?php
declare(strict_types=1);
namespace App\Filament\Resources\WeddingPackages\Tables;
use Filament\Actions\BulkActionGroup;
use Filament\Actions\DeleteAction;
use Filament\Actions\DeleteBulkAction;
use Filament\Actions\EditAction;
use Filament\Tables\Columns\TextColumn;
use Filament\Tables\Table;
class WeddingPackagesTable
{
public static function configure(Table $table): Table
{
return $table
->columns([
TextColumn::make('name')
->label('Nome')
->searchable()
->sortable(),
TextColumn::make('level')
->label('Número / tipo')
->searchable(),
TextColumn::make('published_at')
->label('Publicado em')
->dateTime()
->sortable(),
TextColumn::make('sort_order')
->label('Ordem')
->sortable(),
])
->defaultSort('sort_order')
->filters([
//
])
->recordActions([
EditAction::make(),
DeleteAction::make()
->requiresConfirmation(),
])
->toolbarActions([
BulkActionGroup::make([
DeleteBulkAction::make(),
]),
]);
}
}

View File

@@ -7,16 +7,14 @@ namespace App\Filament\Resources\WeddingPackages;
use App\Filament\Resources\WeddingPackages\Pages\CreateWeddingPackage;
use App\Filament\Resources\WeddingPackages\Pages\EditWeddingPackage;
use App\Filament\Resources\WeddingPackages\Pages\ListWeddingPackages;
use App\Filament\Resources\WeddingPackages\Schemas\WeddingPackageForm;
use App\Filament\Resources\WeddingPackages\Tables\WeddingPackagesTable;
use App\Models\WeddingPackage;
use App\Policies\WeddingPackagePolicy;
use BackedEnum;
use Filament\Forms\Components\DateTimePicker;
use Filament\Forms\Components\TagsInput;
use Filament\Forms\Components\TextInput;
use Filament\Resources\Resource;
use Filament\Schemas\Schema;
use Filament\Support\Icons\Heroicon;
use Filament\Tables\Columns\TextColumn;
use Filament\Tables\Table;
use UnitEnum;
@@ -26,32 +24,39 @@ class WeddingPackageResource extends Resource
protected static ?string $policy = WeddingPackagePolicy::class;
protected static string|BackedEnum|null $navigationIcon = Heroicon::OutlinedHeart;
protected static string|BackedEnum|null $navigationIcon = Heroicon::OutlinedRectangleStack;
protected static ?string $navigationLabel = 'Modalidades de casamento';
protected static ?string $navigationLabel = 'Modalidades';
protected static ?string $modelLabel = 'modalidade';
protected static ?string $pluralModelLabel = 'modalidades';
protected static string|UnitEnum|null $navigationGroup = 'Conteúdo do site';
protected static ?int $navigationSort = 3;
public static function form(Schema $schema): Schema
{
return $schema->components([
TextInput::make('name')->label('Nome')->required()->maxLength(120),
TextInput::make('level')->label('Nível')->required()->maxLength(120),
TextInput::make('summary')->label('Resumo')->required()->maxLength(255),
TagsInput::make('scope_items')->label('Itens de escopo')->required()->columnSpanFull(),
TextInput::make('cta_label')->label('CTA')->required()->maxLength(120),
TextInput::make('sort_order')->label('Ordem')->numeric()->required()->default(0),
DateTimePicker::make('published_at')->label('Publicado em')->seconds(false),
]);
return WeddingPackageForm::configure($schema);
}
public static function table(Table $table): Table
{
return $table->columns([TextColumn::make('name')->label('Nome')->searchable(), TextColumn::make('level')->label('Nível'), TextColumn::make('sort_order')->label('Ordem')->sortable(), TextColumn::make('published_at')->label('Publicado em')->dateTime()->sortable()])->defaultSort('sort_order');
return WeddingPackagesTable::configure($table);
}
public static function getRelations(): array
{
return [];
}
public static function getPages(): array
{
return ['index' => ListWeddingPackages::route('/'), 'create' => CreateWeddingPackage::route('/create'), 'edit' => EditWeddingPackage::route('/{record}/edit')];
return [
'index' => ListWeddingPackages::route('/'),
'create' => CreateWeddingPackage::route('/create'),
'edit' => EditWeddingPackage::route('/{record}/edit'),
];
}
}