404 Not Found

404 Not Found


nginx

Exercício Compreensivo da Fase 1—Landing Page do ShopMetrics

O Exercício Compreensivo da Fase 1 é uma "Revisão Final"—reúne tudo o que você aprendeu nas primeiras seis aulas para entregar uma landing page real e totalmente funcional.

1. O Que Você Vai Aprender


2. A História de Aceitação da Fase 1 do Bob

(1) Problema: Mesmo após completar a Aula 7, ainda não consigo construir uma página completa

Alice terminou as primeiras seis aulas, mas enquanto entende cada conceito individualmente, fica confusa quando são combinados—como o roteamento se conecta aos controladores? Como um controlador passa dados para um Blade? Como escolher entre herança de layout e componentes? Bob disse: "É a diferença entre aprender a nadar e realmente entrar na água—você assistiu a sete aulas teóricas, mas não nadou 25 metros."

(2) Soluções para os Exercícios Compreensivos

Esta aula une rotas, views, controladores, componentes Blade e configuração de ambiente para construir uma landing page completa e totalmente funcional do ShopMetrics do zero—este será o seu primeiro projeto prático.

BASH
# Após esta aula, você terá:
# - Uma landing page com seção hero
# - Uma página de preços com cards de planos
# - Um layout de painel com barra lateral
# - Componentes Blade reutilizáveis
# - Conexão com banco de dados funcionando

(3) Resultado

Após completar o exercício compreensivo, Alice construiu independentemente a landing page do ShopMetrics, e todas as cinco páginas passaram nos testes de aceitação. Bob disse: "A diferença entre 'entender' e 'implementar' é apenas um exercício compreensivo."


3. Design do Blueprint de Rotas

(1) Layout das Páginas

100%
graph LR
    A["/ (Início)"] --> B["/pricing (Preços)"]
    A --> C["/about (Sobre)"]
    A --> D["/contact (Contato)"]
    A --> E["/dashboard (Painel)"]
    E --> F["/dashboard/shops"]
    E --> G["/dashboard/analytics"]

(2) Definições de Rotas

PHP
// routes/web.php
Route::get('/', [HomeController::class, 'index'])->name('home');
Route::get('/pricing', [PricingController::class, 'index'])->name('pricing');
Route::get('/about', [AboutController::class, 'index'])->name('about');
Route::get('/contact', [ContactController::class, 'create'])->name('contact.create');
Route::post('/contact', [ContactController::class, 'store'])->name('contact.store');

Route::middleware('auth')->prefix('dashboard')->name('dashboard.')->group(function () {
    Route::get('/', [DashboardController::class, 'index'])->name('index');
    Route::resource('shops', Dashboard\ShopController::class);
});
Página Rota Controlador Middleware
Início GET / HomeController Nenhum
Preços GET /pricing PricingController Nenhum
Sobre GET /about AboutController Nenhum
Contato GET+POST /contact ContactController Nenhum
Painel GET /dashboard DashboardController auth

(1) ▶ Exemplo: Criar todos os controladores

BASH
php artisan make:controller HomeController
php artisan make:controller PricingController
php artisan make:controller AboutController
php artisan make:controller ContactController
php artisan make:controller Dashboard/DashboardController
php artisan make:controller Dashboard/ShopController --resource

Saída:

TEXT
# Comando executado com sucesso

4. Design de Layout e Componentes

(1) Hierarquia de Layout

TEXT
layouts/app.blade.php        ← Layout principal (navbar + rodapé)
├── layouts/dashboard.blade.php  ← Layout do Painel (barra lateral + conteúdo)
│   └── dashboard/analytics.blade.php
└── home/index.blade.php     ← Landing page

(2) Lista de Componentes

Componente Tipo Arquivo Finalidade
<x-navbar> Anônimo components/navbar.blade.php Barra de Navegação do Site
<x-footer> Anônimo components/footer.blade.php Rodapé do Site
<x-hero> Anônimo components/hero.blade.php Área Hero da Página Inicial
<x-plan-card> Anônimo components/plan-card.blade.php Card de Preços
<x-stat-card> Anônimo components/stat-card.blade.php Card de Estatísticas do Painel
<x-sidebar> Anônimo components/sidebar.blade.php Barra Lateral do Painel

(1) ▶ Exemplo: Criando um Componente Blade

BASH
# Criar arquivos de componente manualmente
mkdir -p resources/views/components
touch resources/views/components/navbar.blade.php
touch resources/views/components/footer.blade.php
touch resources/views/components/hero.blade.php
touch resources/views/components/plan-card.blade.php
touch resources/views/components/stat-card.blade.php
touch resources/views/components/sidebar.blade.php

Saída:

TEXT
# Comando executado com sucesso

5. Implementação do Layout e Componentes Principais

(1) Layout Principal

HTML
<!-- resources/views/layouts/app.blade.php -->
<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>@yield('title', 'ShopMetrics') — Análise de E-commerce</title>
    @vite(['resources/css/app.css', 'resources/js/app.js'])
</head>
<body class="bg-gray-50 text-gray-900">
    <x-navbar />
    <main>
        @yield('content')
    </main>
    <x-footer />
</body>
</html>

(2) Componente de Barra de Navegação

HTML
<!-- resources/views/components/navbar.blade.php -->
<nav class="bg-white shadow-sm" x-data="{ open: false }">
    <div class="max-w-7xl mx-auto px-4 sm:px-6 lg:px-8">
        <div class="flex justify-between h-16">
            <a href="{{ route('home') }}" class="flex items-center font-bold text-xl text-blue-600">
                ShopMetrics
            </a>
            <div class="hidden md:flex items-center space-x-6">
                <a href="{{ route('pricing') }}" class="text-gray-600 hover:text-gray-900">Preços</a>
                <a href="{{ route('about') }}" class="text-gray-600 hover:text-gray-900">Sobre</a>
                @auth
                    <a href="{{ route('dashboard.index') }}" class="btn-primary">Painel</a>
                @else
                    <a href="{{ route('login') }}" class="text-gray-600 hover:text-gray-900">Entrar</a>
                    <a href="{{ route('register') }}" class="btn-primary">Cadastrar</a>
                @endauth
            </div>
            <button @click="open = !open" class="md:hidden">
                <svg class="w-6 h-6" fill="none" stroke="currentColor" viewBox="0 0 24 24">
                    <path x-show="!open" d="M4 6h16M4 12h16M4 18h16"/>
                    <path x-show="open" d="M6 18L18 6M6 6l12 12"/>
                </svg>
            </button>
        </div>
        <div x-show="open" class="md:hidden py-2 space-y-2">
            <a href="{{ route('pricing') }}" class="block py-2 text-gray-600">Preços</a>
            @auth
                <a href="{{ route('dashboard.index') }}" class="block py-2 btn-primary">Painel</a>
            @else
                <a href="{{ route('login') }}" class="block py-2 text-gray-600">Entrar</a>
                <a href="{{ route('register') }}" class="block py-2 btn-primary">Cadastrar</a>
            @endauth
        </div>
    </div>
</nav>

(3) Componente Hero

HTML
<!-- resources/views/components/hero.blade.php -->
@props(['title', 'subtitle', 'cta_text' => 'Começar', 'cta_route' => 'register'])

<section class="bg-gradient-to-r from-blue-600 to-indigo-700 text-white py-20">
    <div class="max-w-7xl mx-auto px-4 text-center">
        <h1 class="text-4xl md:text-6xl font-bold mb-6">{{ $title }}</h1>
        <p class="text-xl md:text-2xl mb-8 text-blue-100">{{ $subtitle }}</p>
        <a href="{{ route($cta_route) }}" class="bg-white text-blue-600 px-8 py-3 rounded-lg font-semibold hover:bg-blue-50 transition">
            {{ $cta_text }}
        </a>
    </div>
</section>

(1) ▶ Exemplo: Componente de Card de Preços

HTML
<!-- resources/views/components/plan-card.blade.php -->
@props(['plan', 'popular' => false])

<div class="bg-white rounded-xl shadow-lg p-8 {{ $popular ? 'ring-2 ring-blue-500 relative' : '' }}">
    @if($popular)
        <span class="absolute -top-3 left-1/2 -translate-x-1/2 bg-blue-500 text-white px-4 py-1 rounded-full text-sm font-semibold">
            Mais Popular
        </span>
    @endif
    <h3 class="text-xl font-bold mb-2">{{ $plan['name'] }}</h3>
    <div class="mb-6">
        <span class="text-4xl font-bold">${{ $plan['price'] }}</span>
        <span class="text-gray-500">/mês</span>
    </div>
    <ul class="space-y-3 mb-8">
        @foreach($plan['features'] as $feature)
            <li class="flex items-center">
                <svg class="w-5 h-5 text-green-500 mr-2" fill="currentColor" viewBox="0 0 20 20">
                    <path d="M16.707 5.293a1 1 0 010 1.414l-8 8a1 1 0 01-1.414 0l-4-4a1 1 0 011.414-1.414L8 12.586l7.293-7.293a1 1 0 011.414 0z"/>
                </svg>
                {{ $feature }}
            </li>
        @endforeach
    </ul>
    <a href="{{ route('register') }}" class="{{ $popular ? 'btn-primary' : 'btn-secondary' }} w-full text-center block">
        Escolher {{ $plan['name'] }}
    </a>
</div>

Saída:

TEXT
// Execução bem-sucedida

6. Implementação do Controlador

(1) ▶ Exemplo: HomeController e PricingController

PHP
// app/Http/Controllers/HomeController.php
class HomeController extends Controller
{
    public function index(): View
    {
        $features = [
            ['icon' => 'chart', 'title' => 'Análise em Tempo Real', 'desc' => 'Acompanhe vendas, pedidos e receita em tempo real.'],
            ['icon' => 'users', 'title' => 'Multi-tenant', 'desc' => 'Dados separados para cada loja de e-commerce.'],
            ['icon' => 'bell', 'title' => 'Alertas Inteligentes', 'desc' => 'Receba notificações quando métricas importantes mudarem.'],
        ];
        return view('home.index', compact('features'));
    }
}

// app/Http/Controllers/PricingController.php
class PricingController extends Controller
{
    public function index(): View
    {
        $plans = [
            ['name' => 'Starter', 'price' => 29, 'features' => ['5 Lojas', '1K Pedidos/mês', 'Análise Básica', 'Suporte por E-mail']],
            ['name' => 'Pro', 'price' => 79, 'features' => ['25 Lojas', '10K Pedidos/mês', 'Análise Avançada', 'Suporte Prioritário', 'Acesso API']],
            ['name' => 'Enterprise', 'price' => 199, 'features' => ['Lojas Ilimitadas', 'Pedidos Ilimitados', 'Análise Personalizada', 'Suporte Dedicado', 'API + Webhooks', 'SSO']],
        ];
        return view('pricing.index', compact('plans'));
    }
}

Saída:

TEXT
// Execução bem-sucedida

(2) ▶ Exemplo: DashboardController

PHP
// app/Http/Controllers/Dashboard/DashboardController.php
class DashboardController extends Controller
{
    public function index(): View
    {
        $user = auth()->user();
        $tenant = $user->tenant;

        $stats = [
            ['label' => 'Receita Total', 'value' => '$' . number_format($tenant->revenue ?? 0), 'trend' => 12.5],
            ['label' => 'Lojas Ativas', 'value' => $tenant->shops()->count(), 'trend' => 3.2],
            ['label' => 'Pedidos Hoje', 'value' => $tenant->orders()->today()->count(), 'trend' => -2.1],
        ];

        return view('dashboard.index', compact('tenant', 'stats'));
    }
}

Saída:

TEXT
// Execução bem-sucedida

7. Configuração de Ambiente e Banco de Dados

BASH
# .env — Configurar MySQL para o ShopMetrics
DB_CONNECTION=mysql
DB_HOST=127.0.0.1
DB_PORT=3306
DB_DATABASE=shopmetrics
DB_USERNAME=root
DB_PASSWORD=secret

# Gerar chave da aplicação e executar migrações
php artisan key:generate
php artisan migrate

# Instalar Breeze para scaffolding de autenticação
composer require laravel/breeze --dev
php artisan breeze:install blade
npm install && npm run build
Etapa Comando Função
1 key:generate Gerar APP_KEY
2 migrate Criar Tabela de Dados
3 breeze:install Gerar Página de Login/Registro
4 npm run build Compilar assets de frontend

8. Exemplo Completo: Página Inicial da Landing Page do ShopMetrics

HTML
<!-- ============================================
     Completo: Landing Page do ShopMetrics
     Abrange: layout, componentes, rotas, passagem de dados
     ============================================ -->

<!-- resources/views/home/index.blade.php -->
@extends('layouts.app')

@section('title', 'ShopMetrics — Plataforma de Análise de E-commerce')

@section('content')
    <!-- Seção Hero -->
    <x-hero
        title="Acompanhe o Desempenho do Seu E-commerce"
        subtitle="Análise em tempo real para plataformas de e-commerce multi-tenant. Monitore receita, pedidos e insights de clientes — tudo em um painel."
        cta_text="Iniciar Teste Gratuito"
        cta_route="register"
    />

    <!-- Seção de Funcionalidades -->
    <section class="py-20 bg-white">
        <div class="max-w-7xl mx-auto px-4">
            <h2 class="text-3xl font-bold text-center mb-12">Por que ShopMetrics?</h2>
            <div class="grid md:grid-cols-3 gap-8">
                @foreach($features as $feature)
                    <div class="text-center p-6">
                        <div class="w-12 h-12 bg-blue-100 rounded-lg mx-auto mb-4 flex items-center justify-center">
                            <span class="text-blue-600 text-2xl">
                                @if($feature['icon'] === 'chart')📈@elseif($feature['icon'] === 'users')👥@else🔔@endif
                            </span>
                        </div>
                        <h3 class="text-xl font-semibold mb-2">{{ $feature['title'] }}</h3>
                        <p class="text-gray-600">{{ $feature['desc'] }}</p>
                    </div>
                @endforeach
            </div>
        </div>
    </section>

    <!-- Seção CTA -->
    <section class="py-16 bg-gray-100">
        <div class="max-w-4xl mx-auto text-center px-4">
            <h2 class="text-3xl font-bold mb-4">Pronto para crescer seu negócio?</h2>
            <p class="text-gray-600 mb-8">Junte-se a mais de 500 comerciantes que confiam no ShopMetrics para suas análises.</p>
            <div class="flex gap-4 justify-center">
                <a href="{{ route('register') }}" class="btn-primary">Iniciar Teste Gratuito</a>
                <a href="{{ route('pricing') }}" class="btn-secondary">Ver Preços</a>
            </div>
        </div>
    </section>
@endsection

❓ Perguntas Frequentes

P Landing pages exigem autenticação?
R A página inicial, página de preços e página sobre não exigem autenticação e são acessíveis a qualquer pessoa; o painel é protegido pelo middleware auth. O Breeze já cuidou automaticamente das rotas de login/registro.
P Como escolher entre herança de layout e componentes?
R Use herança de layout para estruturas de nível de página (como uma navbar e rodapé envolvendo o conteúdo); use componentes Blade para fragmentos de UI reutilizáveis (como cards, botões e formulários). Layouts definem o "esqueleto", enquanto componentes preenchem a "carne".
P O que devo fazer se o Tailwind CSS não estiver funcionando?
R Certifique-se de que está executando npm run dev (hot reload no modo de desenvolvimento) ou npm run build (build de produção), e verifique se a configuração content no tailwind.config.js inclui os caminhos para todos os arquivos Blade.
P O x-show do Alpine.js é o mesmo que o v-show do Vue?
R Servem a um propósito semelhante, mas Alpine.js é uma biblioteca independente leve (15 KB) que não requer processo de build; o v-show do Vue requer uma instância Vue. Use Alpine.js para interações simples e Vue para aplicações de página única complexas.
P Qual é a diferença entre Breeze e Jetstream?
R Breeze é um scaffold de autenticação leve (escolha entre Blade, Inertia ou Livewire); tem menos código e é fácil de entender. Jetstream é uma solução mais rica em recursos (incluindo gerenciamento de equipes e 2FA), mas é mais complexo. Recomendamos começar com Breeze.
P Como lidar com dados compartilhados entre múltiplos controladores?
R Use View Composer para injetar automaticamente dados compartilhados (como a lista de lojas na barra de navegação), ou crie uma classe Service para compartilhar lógica entre controladores. Não compartilhe dados na classe base do controlador.

📖 Resumo


📝 Exercícios

  1. Tarefa Básica (⭐): Complete as cinco páginas da landing page do ShopMetrics (Início/Preços/Sobre/Contato/Painel), garantindo que todas as rotas estejam acessíveis, a herança de layout funcione corretamente e os links da barra de navegação estejam corretos.

  2. Exercício Avançado (⭐⭐): Adicione um alternador de pagamento mensal/anual à página de preços (usando Alpine.js). O preço anual deve ter 20% de desconto, e o preço do card deve transitar com uma animação ao alternar entre as opções de pagamento.

  3. Desafio (⭐⭐⭐): Implemente um menu dinâmico na barra lateral do painel—exiba itens de menu diferentes com base no papel do usuário (admin/tenant_owner/analyst), usando View Composer para recuperar configurações de permissão do banco de dados.

Web-Tutorial.com

Equipe Técnica Web-Tutorial

Uma plataforma de tutoriais mantida por diversos desenvolvedores. Cada tutorial é escrito e revisado por profissionais da área correspondente. Trabalhamos para manter nosso conteúdo preciso e confiável — se encontrar algum problema, avise-nos.

100%