Pular para o conteúdo
He4rt / Docs
Toggle sidebar
Interno noindex

Referência técnica — acessível, mas fora dos buscadores.

Discord
Engenharia Leitura · 9 min
Proposto · 0%
20/06/2026 documento interno · noindex

Página de redes sociais (link-tree) — Implementation Plan

Artefato de planejamento de 20/06/2026 — pode não refletir o estado atual do código.

For agentic workers: REQUIRED SUB-SKILL: Use superpowers:subagent-driven-development (recommended) or superpowers:executing-plans to implement this plan task-by-task. Steps use checkbox (- [ ]) syntax for tracking.

Goal: Publicar uma página link-tree em GET /redes com os 6 links de rede social da He4rt, reaproveitando a linha de batimento (ECG) animada da capa do deck de retrospectiva.

Architecture: Página Livewire full-page no módulo portal (camada de apresentação), seguindo o padrão de Homepage/CommunityRetrospectivePage. Os links são um array tipado de DTO SocialLink exposto pelo componente; a blade itera e renderiza âncoras externas. A ECG é reaproveitada copiando só o markup do <svg> e definindo animação própria scoped na página (sem tocar no retrospective.css).

Tech Stack: Laravel 13, Livewire 4, Filament 5 (icon component), Tailwind v4, Pest 4, owenvoke/blade-fontawesome (ícones de marca).

Global Constraints


Files:

Interfaces:

app-modules/portal/tests/Feature/SocialLinksPageTest.php:

<?php

declare(strict_types=1);

use function Pest\Laravel\get;

it('responde 200 em /redes', function (): void {
    get('/redes')->assertOk();
});

it('resolve a rota nomeada social-links para /redes', function (): void {
    expect(route('social-links', absolute: false))->toBe('/redes');
});

it('renderiza os seis links com rótulos e URLs corretas', function (): void {
    get('/redes')
        ->assertOk()
        ->assertSee('Discord')
        ->assertSee('X (Twitter)')
        ->assertSee('LinkedIn')
        ->assertSee('WhatsApp')
        ->assertSee('GitHub')
        ->assertSee('Site oficial')
        ->assertSee('https://discord.gg/invite/he4rt')
        ->assertSee('https://x.com/He4rtDevs')
        ->assertSee('https://www.linkedin.com/company/he4rt/')
        ->assertSee('https://chat.whatsapp.com/EBKjYxIodpe1x5LLExbTzK')
        ->assertSee('https://github.com/he4rt')
        ->assertSee('https://heartdevs.com/');
});

it('abre cada link externo em nova aba com rel seguro', function (): void {
    $html = get('/redes')->getContent();

    expect(substr_count($html, 'target="_blank"'))->toBe(6);
    expect(substr_count($html, 'rel="noopener noreferrer"'))->toBe(6);
});

Run: php artisan test --compact --filter=SocialLinksPage Expected: FAIL — rota /redes inexistente (404) e classe SocialLinksPage não encontrada.

app-modules/portal/src/DTOs/SocialLink.php:

<?php

declare(strict_types=1);

namespace He4rt\Portal\DTOs;

final readonly class SocialLink
{
    public function __construct(public string $label, public string $url, public string $icon, public string $accent) {}
}

app-modules/portal/src/Livewire/SocialLinksPage.php:

<?php

declare(strict_types=1);

namespace He4rt\Portal\Livewire;

use He4rt\Portal\DTOs\SocialLink;
use Illuminate\Contracts\View\View;
use Livewire\Attributes\Layout;
use Livewire\Attributes\Title;
use Livewire\Component;

#[Layout('portal::components.layouts.app')]
#[Title('Nossas redes')]
final class SocialLinksPage extends Component
{
    /**
     * @return list<SocialLink>
     */
    public function links(): array
    {
        return [
            new SocialLink('Discord', 'https://discord.gg/invite/he4rt', 'fab-discord', '#5865F2'),
            new SocialLink('X (Twitter)', 'https://x.com/He4rtDevs', 'fab-x-twitter', '#FFFFFF'),
            new SocialLink('LinkedIn', 'https://www.linkedin.com/company/he4rt/', 'fab-linkedin', '#0A66C2'),
            new SocialLink('WhatsApp', 'https://chat.whatsapp.com/EBKjYxIodpe1x5LLExbTzK', 'fab-whatsapp', '#25D366'),
            new SocialLink('GitHub', 'https://github.com/he4rt', 'fab-github', '#FFFFFF'),
            new SocialLink('Site oficial', 'https://heartdevs.com/', 'fas-globe', '#782BF1'),
        ];
    }

    public function render(): View
    {
        return view('portal::social-links', [
            'links' => $this->links(),
        ]);
    }
}

app-modules/portal/resources/views/social-links.blade.php (um único elemento raiz):

<div class="mx-auto flex w-full max-w-[480px] flex-col gap-3 px-6 py-16">
    @foreach ($links as $link)
        <a
            href="{{ $link->url }}"
            target="_blank"
            rel="noopener noreferrer"
            class="border-outline-low bg-elevation-01dp flex items-center gap-4 rounded-xl border px-5 py-4 text-white"
        >
            <x-filament::icon :icon="$link->icon" class="h-6 w-6 shrink-0" />
            <span class="flex-1 text-left font-medium">{{ $link->label }}</span>
        </a>
    @endforeach
</div>

Em app-modules/portal/src/PortalServiceProvider.php, adicionar o use e a rota.

Adicionar ao bloco de imports:

use He4rt\Portal\Livewire\SocialLinksPage;

Dentro de boot(), logo após a linha Route::get('/', Homepage::class);:

Route::get('/redes', SocialLinksPage::class)->name('social-links');

Run: php artisan test --compact --filter=SocialLinksPage Expected: PASS (4 testes verdes).

Run: vendor/bin/pint --dirty --format agent Expected: arquivos formatados sem erros.

git add app-modules/portal/src/DTOs/SocialLink.php \
        app-modules/portal/src/Livewire/SocialLinksPage.php \
        app-modules/portal/resources/views/social-links.blade.php \
        app-modules/portal/src/PortalServiceProvider.php \
        app-modules/portal/tests/Feature/SocialLinksPageTest.php
git commit -m "feat(portal): add /redes social link-tree page"

Task 2: Identidade visual — logo, linha ECG animada e acentos de marca

Files:

Interfaces:

Acrescentar ao final de app-modules/portal/tests/Feature/SocialLinksPageTest.php:

it('exibe a linha de batimento (ECG) animada', function (): void {
    get('/redes')->assertSee('links-ecg', false);
});

it('exibe a tagline e o acento de marca de cada link', function (): void {
    $html = get('/redes')->getContent();

    expect($html)->toContain('Conecte-se com a comunidade He4rt Devs');
    expect(substr_count($html, '--accent:'))->toBe(6);
});

Run: php artisan test --compact --filter=SocialLinksPage Expected: os dois novos testes FALHAM (links-ecg, tagline e --accent: ainda não existem); os 4 da Task 1 continuam passando.

Conteúdo final de app-modules/portal/resources/views/social-links.blade.php:

<div class="relative min-h-screen overflow-hidden">
    <style>
        .links-ecg {
            width: 100%;
            max-width: 320px;
            height: 48px;
            display: block;
            margin: 12px auto 28px;
        }
        .links-ecg path {
            fill: none;
            stroke: var(--primary, #782bf1);
            stroke-width: 2.4;
            stroke-linecap: round;
            stroke-linejoin: round;
            filter: drop-shadow(0 0 7px rgba(120, 43, 241, 0.85));
            stroke-dasharray: 1500;
            stroke-dashoffset: 1500;
            animation: links-ecg-draw 1.8s 0.3s ease-out forwards;
        }
        @keyframes links-ecg-draw {
            to {
                stroke-dashoffset: 0;
            }
        }
        .link-card {
            transition:
                border-color 0.2s,
                color 0.2s,
                transform 0.2s,
                background-color 0.2s;
        }
        .link-card:hover {
            border-color: var(--accent);
            color: var(--accent);
            transform: translateY(-2px);
            background-color: color-mix(in srgb, var(--accent) 8%, transparent);
        }
        .link-card:hover .link-arrow {
            transform: translateX(4px);
        }
        @media (prefers-reduced-motion: reduce) {
            .links-ecg path {
                animation: none;
                stroke-dashoffset: 0;
            }
            .link-card,
            .link-card:hover {
                transform: none;
            }
        }
    </style>

    <div class="mx-auto flex w-full max-w-[480px] flex-col items-center px-6 py-16">
        <x-portal::logo size="lg" />

        <p class="text-text-medium mt-4 text-center text-sm">Conecte-se com a comunidade He4rt Devs</p>

        <svg class="links-ecg" viewBox="0 0 560 60" preserveAspectRatio="none" aria-hidden="true">
            <path
                d="M0 34 H210 l10 0 l8 -7 l11 14 l13 -34 l15 50 l11 -22 l9 0 H340 l9 0 l8 -6 l9 12 l12 -28 l13 40 l10 -16 l8 0 H560"
            />
        </svg>

        <nav class="flex w-full flex-col gap-3" aria-label="Redes sociais da He4rt">
            @foreach ($links as $link)
                <a
                    href="{{ $link->url }}"
                    target="_blank"
                    rel="noopener noreferrer"
                    style="--accent: {{ $link->accent }}"
                    class="link-card border-outline-low bg-elevation-01dp flex items-center gap-4 rounded-xl border px-5 py-4 text-white"
                >
                    <x-filament::icon :icon="$link->icon" class="h-6 w-6 shrink-0" />
                    <span class="flex-1 text-left font-medium">{{ $link->label }}</span>
                    <x-filament::icon
                        icon="heroicon-s-arrow-up-right"
                        class="link-arrow h-4 w-4 shrink-0 transition-transform"
                    />
                </a>
            @endforeach
        </nav>
    </div>
</div>

Nota de implementação: o x-portal::logo já renderiza o coração He4rt (SVG currentColor) + wordmark "He4rt Devs", centralizado pelo items-center do container. O acento de marca usa --accent por link (evita classes Tailwind dinâmicas que o JIT não escaneia). A animação respeita prefers-reduced-motion.

Run: php artisan test --compact --filter=SocialLinksPage Expected: PASS (6 testes verdes).

Usar a skill /run (ou abrir a app local) e navegar até /redes. Conferir: logo no topo, tagline, a linha ECG desenhando uma única vez no load, os 6 botões com acento de marca no hover e seta deslizando. Conferir responsivo em largura mobile.

Run: vendor/bin/pint --dirty --format agent (O pre-commit já roda Prettier em *.blade.php/*.css via lint-staged.) Expected: sem erros.

git add app-modules/portal/resources/views/social-links.blade.php \
        app-modules/portal/tests/Feature/SocialLinksPageTest.php
git commit -m "feat(portal): style /redes page with He4rt logo and animated ECG line"

Notas de execução