# Changelog — Mangolê

> **Formato baseado em Keep a Changelog (https://keepachangelog.com/).**

---

## [FASE 15] — Database Foundation & Testing Hardening

> **Data:** 2026-07-21
> **Validação:** 46 tests passed, 110 assertions, migrate:fresh --seed OK

### Added

- Foundation migration `2026_07_20_230000_create_foundation_tables.php` com `Schema::hasTable()` guards (12 tabelas: stores, products, categories, campaigns, campaign_assets, campaign_ctas, news_articles, partners, vacancies, settings, seo_metadata, team_members)
- 15 factories: Store, Product, Category, Brand, Campaign, NewsArticle, Partner, Vacancy, Opportunity, BusinessInquiry, TeamMember, StoreHour, StoreService, Province, Municipality
- Trait `HasFactory` adicionado a 15 Models
- Helpers `app/helpers.php` com função `safe_html()` para sanitização de HTML rich text
- 5 testes: DatabaseFoundation (12 tabelas, FK, modelos), ContentModel (scopes, estados), BusinessInquiry (criação, scopes), RouteResponse (12 rotas públicas), ModelCast (10 enums, labels)
- Cache com `Cache::remember()` na homepage (categories, campaigns, news, stores count, provinces)
- Acessibilidade: `aria-current="page"`, `aria-expanded`, `aria-controls`, `aria-label`
- Método `label()` em todos os 10 enums
- Casos `Pending` (`pending`) e `Read` (`read`) em `InquiryStatus`

### Changed

- StoreSeeder: removido campo legado `province_code` dos dados
- Migration `000012_convert_team_members_category_to_string`: agora apenas executa em MySQL (`DB::getDriverName() === 'mysql'`)
- `composer.json` autoload: `"files": ["app/helpers.php"]`

### Fixed

- XSS no mapa Leaflet: `{{ $store->name }}` → `@json($store->name)` em `stores/show.blade.php`
- Rich text sem sanitização: `{!! $article->content !!}` → `{!! safe_html($article->content) !!}` em views de news e opportunities
- Factory de `NewsArticle`: slug gerado por `booted()`, não por factory definition
- `ExampleTest`: adicionado `$this->seed()` antes de `$this->get('/')`
- `BusinessInquiryTest::test_pending_scope`: alinhado com scope real (`new`, `in_review`)
- `ModelCastTest::test_inquiry_status_enum_values`: removido `Replied` inexistente

### Validation

- `php artisan migrate:fresh --seed`: ✅ 24 migrations, 12 seeders
- `php artisan test --compact`: ✅ 46 tests, 110 assertions, 0 failures
- `npm run build`: ✅ Vite 8.1.5, 6.56s
- `php artisan route:list`: ✅ 52 rotas

---

## [FASE 16] — Security, Admin & Production Hardening

> **Data:** 2026-07-21
> **Validação:** 68 tests passed, 209 assertions, migrate:fresh --seed OK

### Added

- Rate limiting: `throttle:form` (5/min por IP) na POST `/para-negocios`, configurado em `AppServiceProvider::boot()`
- 20 testes Filament: AdminLogin (3), StoreResource (5), ContentResource (12)
- Testes de rate limiting (2): form burst → 429, GET ilimitado → 200

### Changed

- Imports Filament corrigidos em 16 Resources (`Forms\Components\Section` → `Schemas\Components\Section`, `Tables\Actions\EditAction` → `Actions\EditAction`)
- `heroicon-o-handshake` substituído por `heroicon-o-user-group` (ícone inexistente)
- Icons removidos dos navigation groups (conflito com icons dos resources)
- Documentação: `docs/DATA_MODEL.md` e `docs/DOMAIN_ARCHITECTURE.md` actualizados
- `fontaine` instalado — warning Vite eliminado, build limpo

### Fixed

- `product_families` auditado — apenas referenciado na migration 000005 e docs; sem alterações necessárias

### Validation

- `php artisan test --compact`: ✅ 68 tests, 209 assertions, 0 failures
- `php artisan migrate:fresh --seed`: ✅ 24 migrations, 12 seeders
- `npm run build`: ✅ 6.12s, sem warnings

---

## [FASE 18] — Settings → Homepage

> **Data:** 2026-07-21
> **Validação:** 68 tests, 209 assertions, migrate:fresh --seed OK

### Added

- ~40 novas settings no grupo `home` (todas as 8 secções da homepage: Hero, Localizador, Categorias, Campanhas, Experiência, Negócios, Rede, Notícias, CTA Final)
- Cache dedicada para settings: `settings.home`, `settings.contact`, `settings.seo` (3600s TTL)
- Grupos `company`, `seo`, `operational` adicionados ao `Select::make('group')` no SettingResource

### Changed

- `welcome.blade.php`: todo o texto hardcoded substituído por `$homeSettings['key'] ?? 'fallback'`
- Meta tags (title/description) passaram a usar `seo.default_title` e `seo.default_description`
- Telefone e WhatsApp: links `tel:` e `wa.me` passaram a usar `contact.phone` e `contact.whatsapp`
- `StoreResource::phone`: removido `->tel()` (validação rejeitava factory data)

### Fixed

- Removido `Cache::remember` das queries de modelos Eloquent na homepage: driver `database` serializava Collections e causava erro `incomplete object` no PHP 8.5 ao unserializar

### Validation

- `php artisan test --compact`: ✅ 68 tests, 209 assertions, 0 failures
- `php artisan migrate:fresh --seed`: ✅ 24 migrations, 12 seeders
- `npm run build`: ✅ 9.96s, sem warnings

## [FASE 19] — Homepage Editorial Architecture & Content Control

> **Data:** 2026-07-21
> **Validação:** 68 tests, 209 assertions, migrate:fresh --seed OK, npm run build OK

### Added

- `app/Services/HomepageService.php`: service centralizado que prepara todos os dados da homepage (models + settings)
- `app/Observers/SettingObserver.php`: invalida cache do grupo sempre que uma setting é criada/actualizada/apagada
- Cache reintroduzida para settings: `Cache::remember("settings.{group}", 3600, ...)`
- `docs/PHASE_19_ARCHITECTURE_AUDIT.md`: auditoria arquitectural completa da homepage

### Changed

- `routes/web.php`: rota `/` passou a usar `HomepageService::build()` para preparar os dados da view
- `welcome.blade.php`: removido todo o `@php` block com queries e processamento inline — dados vêm injectados pelo serviço
- `AppServiceProvider.php`: registado `SettingObserver`

### Architectural Decisions

- **Composição fixa** (Estratégia A): a homepage mantém secções fixas, sem Page Builder
- **Nenhuma setting migrada para entidade**: todas as ~45 settings continuam como `Setting` — são texto editorial sem identidade própria
- **Labels de interface permanecem hardcoded**: "Pesquisar", "Ligar", "Detalhes" etc. são UI, não conteúdo editorial

### Validation

- `php artisan test --compact`: ✅ 68 tests, 209 assertions, 0 failures
- `php artisan migrate:fresh --seed`: ✅ 24 migrations, 12 seeders
- `npm run build`: ✅ 5.77s, sem warnings

## [FASE 20] — Homepage Hero Carousel Editorial

> **Data:** 2026-07-21
> **Validação:** 79 tests, 242 assertions, migrate:fresh --seed OK, npm run build OK

### Added

- Nova entidade `HomepageSlide` com migration, model, factory, seeder (4 slides)
- `app/Filament/Resources/HomepageSlideResource.php`: Resource Filament com 5 secções (Conteúdo, CTAs, Imagem, Apresentação, Programação)
- `app/Observers/HomepageSlideObserver.php`: invalida cache `homepage.hero.slides` no saved/deleted
- `app/Services/HomepageService.php`: método `heroSlides()` com cache e fallback editorial
- `resources/views/components/homepage/hero-carousel.blade.php`: componente Blade com 5 variantes visuais (brand, brand-dark, accent, danger, white)
- `resources/js/app.js`: Alpine.js + componente `heroCarousel` (autoplay 6s, pause ao hover, teclado, dots)
- `alpinejs` como dependência npm
- `docs/PHASE_20_HOMEPAGE_HERO_CAROUSEL.md`: documentação completa

### Changed

- `resources/views/welcome.blade.php`: hero estática substituída por `<x-homepage.hero-carousel :slides="$heroSlides" />`
- `database/seeders/DatabaseSeeder.php`: chamada ao `HomepageSlideSeeder`
- `app/Providers/AppServiceProvider.php`: registado `HomepageSlideObserver`
- `tests/Feature/DatabaseFoundationTest.php`: `homepage_slides` adicionado à lista de tabelas obrigatórias

### Architectural Decisions

- **Nova entidade (`HomepageSlide`) em vez de reutilizar `Campaign`**: slides têm requisitos específicos (imagem única, 2 CTAs, variante de fundo, alinhamento) que não se alinham com a complexidade da entidade Campaign
- **Cache com arrays puros**: driver `database` serializa com serialize/unserialize; Eloquent models podem causar erro no PHP 8.5, arrays são seguros
- **Fallback hardcoded**: homepage nunca quebra mesmo sem slides criados
- **Alpine.js**: leve (~12KB), zero dependências, integração natural com Blade

### Tests

- 11 novos testes em `tests/Feature/HomepageSlideTest.php`
- Cobertura: zero slides, um slide, múltiplos, activos/inactivos, período, ordenação, service, observer, CRUD Filament

### Validation

- `php artisan test --compact`: ✅ 79 tests, 242 assertions, 0 failures
- `php artisan migrate:fresh --seed`: ✅ 25 migrations, 13 seeders
- `npm run build`: ✅ 12.05s, sem warnings

---

## [FASES ANTERIORES 0–14]

Para o registo das fases anteriores, consultar `docs/REBUILD_ROADMAP.md`.
