Claude-skill-registry email-components
Componentes de UI para geração e visualização de e-mails seguindo o padrão de components do Easy Budget.
git clone https://github.com/majiayu000/claude-skill-registry
T=$(mktemp -d) && git clone --depth=1 https://github.com/majiayu000/claude-skill-registry "$T" && mkdir -p ~/.claude/skills && cp -r "$T/skills/data/email-components" ~/.claude/skills/majiayu000-claude-skill-registry-email-components && rm -rf "$T"
skills/data/email-components/SKILL.mdComponentes de UI para E-mails
Esta skill define os componentes Blade específicos para a geração e visualização de e-mails no Easy Budget, seguindo o padrão de components estabelecido no sistema.
Estrutura de Components
resources/views/components/ ├── email/ │ ├── email-header.blade.php # Cabeçalho padrão de e-mails │ ├── email-footer.blade.php # Rodapé padrão de e-mails │ ├── email-layout.blade.php # Layout base de e-mails │ ├── email-budget.blade.php # Template de e-mail de orçamento │ ├── email-invoice.blade.php # Template de e-mail de fatura │ ├── email-reminder.blade.php # Template de e-mail de lembrete │ ├── email-welcome.blade.php # Template de e-mail de boas-vindas │ ├── email-verification.blade.php # Template de e-mail de verificação │ ├── email-password.blade.php # Template de e-mail de senha │ ├── email-support.blade.php # Template de e-mail de suporte │ └── email-styles.blade.php # Estilos CSS para e-mails └── ...
1. Email Header Component
Componente para cabeçalho padrão de e-mails.
Uso Básico
<x-email.email-header :title="$title" :company="$company" />
Parâmetros
| Parâmetro | Tipo | Descrição | Padrão |
|---|---|---|---|
| | Título do e-mail | Obrigatório |
| | Dados da empresa | |
| | Exibir logo da empresa | |
Estrutura
@props([ 'title', 'company' => [], 'showLogo' => true ]) <!DOCTYPE html> <html lang="pt-BR"> <head> <meta charset="UTF-8"> <meta name="viewport" content="width=device-width, initial-scale=1.0"> <title>{{ $title }}</title> <x-email.email-styles /> </head> <body> <div class="email-container"> <!-- Cabeçalho --> <header class="email-header"> <div class="header-content"> @if($showLogo && !empty($company['logo'])) <div class="company-logo"> <img src="{{ $company['logo_url'] ?? asset('storage/' . $company['logo']) }}" alt="{{ $company['name'] ?? 'Easy Budget' }}" style="max-height: 60px;"> </div> @endif <div class="company-info"> <h1 class="company-name">{{ $company['name'] ?? 'Easy Budget' }}</h1> <div class="company-tagline">{{ $company['tagline'] ?? 'Sua solução de gestão empresarial' }}</div> </div> </div> </header>
2. Email Footer Component
Componente para rodapé padrão de e-mails.
Uso Básico
<x-email.email-footer :company="$company" :unsubscribeUrl="$unsubscribeUrl" />
Parâmetros
| Parâmetro | Tipo | Descrição | Padrão |
|---|---|---|---|
| | Dados da empresa | |
| | URL para cancelamento de inscrição | |
| | Exibir links sociais | |
Estrutura
@props([ 'company' => [], 'unsubscribeUrl' => null, 'showSocial' => true ]) <!-- Rodapé --> <footer class="email-footer"> <div class="footer-content"> <div class="footer-section"> <h3>{{ $company['name'] ?? 'Easy Budget' }}</h3> <p class="footer-description"> {{ $company['description'] ?? 'Sua solução de gestão empresarial completa.' }} </p> </div> <div class="footer-section"> <h4>Contato</h4> <div class="contact-info"> @if(!empty($company['email'])) <div class="contact-item"> <i class="bi bi-envelope"></i> <a href="mailto:{{ $company['email'] }}">{{ $company['email'] }}</a> </div> @endif @if(!empty($company['phone'])) <div class="contact-item"> <i class="bi bi-telephone"></i> <span>{{ $company['phone'] }}</span> </div> @endif @if(!empty($company['website'])) <div class="contact-item"> <i class="bi bi-globe"></i> <a href="{{ $company['website'] }}" target="_blank">{{ $company['website'] }}</a> </div> @endif </div> </div> @if($showSocial && !empty($company['social'])) <div class="footer-section"> <h4>Siga-nos</h4> <div class="social-links"> @foreach($company['social'] as $social) <a href="{{ $social['url'] }}" target="_blank" class="social-link"> <i class="bi bi-{{ $social['icon'] }}"></i> </a> @endforeach </div> </div> @endif </div> <div class="footer-bottom"> <div class="footer-text"> © {{ date('Y') }} {{ $company['name'] ?? 'Easy Budget' }}. Todos os direitos reservados. </div> @if($unsubscribeUrl) <div class="footer-text"> <a href="{{ $unsubscribeUrl }}" class="unsubscribe-link">Cancelar inscrição</a> </div> @endif </div> </footer> </div> </body> </html>
3. Email Layout Component
Componente para layout base de e-mails.
Uso Básico
<x-email.email-layout :title="$title" :company="$company" :content="$content" />
Parâmetros
| Parâmetro | Tipo | Descrição | Padrão |
|---|---|---|---|
| | Título do e-mail | Obrigatório |
| | Dados da empresa | |
| | Conteúdo do e-mail | Obrigatório |
| | Exibir cabeçalho | |
| | Exibir rodapé | |
Estrutura
@props([ 'title', 'company' => [], 'content', 'showHeader' => true, 'showFooter' => true ]) <x-email.email-header :title="$title" :company="$company" /> <!-- Conteúdo Principal --> <main class="email-main"> {!! $content !!} </main> <x-email.email-footer :company="$company" />
4. Email Budget Component
Componente para template de e-mail de orçamento.
Uso Básico
<x-email.email-budget :budget="$budget" :customer="$customer" :company="$company" />
Parâmetros
| Parâmetro | Tipo | Descrição | Padrão |
|---|---|---|---|
| | Modelo do orçamento | Obrigatório |
| | Cliente do orçamento | Obrigatório |
| | Dados da empresa | |
| | URL de ação (visualizar orçamento) | |
Estrutura
@props([ 'budget', 'customer', 'company' => [], 'actionUrl' => null ]) <x-email.email-header :title="'Orçamento #' . $budget->code . ' - ' . $customer->display_name" :company="$company" /> <!-- Conteúdo Principal --> <main class="email-main"> <div class="email-hero"> <h2 class="hero-title">Novo Orçamento Disponível</h2> <p class="hero-subtitle">Olá {{ $customer->display_name }},</p> </div> <div class="email-content"> <div class="content-section"> <h3>Detalhes do Orçamento</h3> <div class="budget-details"> <div class="detail-row"> <span class="detail-label">Número:</span> <span class="detail-value">{{ $budget->code }}</span> </div> <div class="detail-row"> <span class="detail-label">Data:</span> <span class="detail-value">{{ $budget->created_at->format('d/m/Y') }}</span> </div> <div class="detail-row"> <span class="detail-label">Validade:</span> <span class="detail-value">{{ $budget->due_date?->format('d/m/Y') ?? 'Indeterminado' }}</span> </div> <div class="detail-row"> <span class="detail-label">Status:</span> <span class="status-badge status-{{ $budget->status }}"> {{ ucfirst($budget->status) }} </span> </div> <div class="detail-row"> <span class="detail-label">Total:</span> <span class="detail-value total-amount"> R$ {{ number_format($budget->total, 2, ',', '.') }} </span> </div> </div> </div> <!-- Serviços --> @if($budget->services->isNotEmpty()) <div class="content-section"> <h3>Serviços Incluídos</h3> <div class="services-list"> @foreach($budget->services as $service) <div class="service-item"> <div class="service-info"> <div class="service-code">{{ $service->code }}</div> <div class="service-description">{{ $service->description }}</div> </div> <div class="service-price"> R$ {{ number_format($service->total - $service->discount, 2, ',', '.') }} </div> </div> @endforeach </div> </div> @endif <!-- Observações --> @if($budget->description) <div class="content-section"> <h3>Observações</h3> <div class="observations"> {{ $budget->description }} </div> </div> @endif <!-- Ações --> @if($actionUrl) <div class="content-section"> <h3>Próximos Passos</h3> <div class="action-buttons"> <a href="{{ $actionUrl }}" class="btn-primary"> Visualizar Orçamento </a> <a href="{{ route('provider.budgets.index') }}" class="btn-secondary"> Ver Todos os Orçamentos </a> </div> </div> @endif <!-- Termos --> <div class="content-section"> <h3>Termos e Condições</h3> <div class="terms"> <p> Este orçamento tem validade de 30 dias a partir da data de emissão. Para aceitar este orçamento, por favor entre em contato conosco. </p> </div> </div> </div> </main> <x-email.email-footer :company="$company" />
5. Email Invoice Component
Componente para template de e-mail de fatura.
Uso Básico
<x-email.email-invoice :invoice="$invoice" :customer="$customer" :company="$company" />
Parâmetros
| Parâmetro | Tipo | Descrição | Padrão |
|---|---|---|---|
| | Modelo da fatura | Obrigatório |
| | Cliente da fatura | Obrigatório |
| | Dados da empresa | |
| | URL de pagamento | |
Estrutura
@props([ 'invoice', 'customer', 'company' => [], 'paymentUrl' => null ]) <x-email.email-header :title="'Fatura #' . $invoice->code . ' - ' . $customer->display_name" :company="$company" /> <!-- Conteúdo Principal --> <main class="email-main"> <div class="email-hero"> <h2 class="hero-title">Nova Fatura Disponível</h2> <p class="hero-subtitle">Olá {{ $customer->display_name }},</p> </div> <div class="email-content"> <div class="content-section"> <h3>Detalhes da Fatura</h3> <div class="invoice-details"> <div class="detail-row"> <span class="detail-label">Número:</span> <span class="detail-value">{{ $invoice->code }}</span> </div> <div class="detail-row"> <span class="detail-label">Data de Emissão:</span> <span class="detail-value">{{ $invoice->created_at->format('d/m/Y') }}</span> </div> <div class="detail-row"> <span class="detail-label">Data de Vencimento:</span> <span class="detail-value">{{ $invoice->due_date?->format('d/m/Y') ?? 'N/A' }}</span> </div> <div class="detail-row"> <span class="detail-label">Status:</span> <span class="status-badge status-{{ $invoice->status }}"> {{ ucfirst($invoice->status) }} </span> </div> <div class="detail-row"> <span class="detail-label">Total:</span> <span class="detail-value total-amount"> R$ {{ number_format($invoice->total, 2, ',', '.') }} </span> </div> </div> </div> <!-- Itens da Fatura --> @if($invoice->items->isNotEmpty()) <div class="content-section"> <h3>Itens da Fatura</h3> <div class="invoice-items"> @foreach($invoice->items as $item) <div class="item-row"> <div class="item-description">{{ $item->description }}</div> <div class="item-quantity">{{ $item->quantity }}</div> <div class="item-price">R$ {{ number_format($item->unit_price, 2, ',', '.') }}</div> <div class="item-total">R$ {{ number_format($item->total, 2, ',', '.') }}</div> </div> @endforeach </div> <div class="invoice-summary"> <div class="summary-row"> <span class="summary-label">Subtotal:</span> <span class="summary-value">R$ {{ number_format($invoice->subtotal, 2, ',', '.') }}</span> </div> @if($invoice->discount > 0) <div class="summary-row"> <span class="summary-label">Desconto:</span> <span class="summary-value">- R$ {{ number_format($invoice->discount, 2, ',', '.') }}</span> </div> @endif <div class="summary-row total-row"> <span class="summary-label">Total:</span> <span class="summary-value">R$ {{ number_format($invoice->total, 2, ',', '.') }}</span> </div> </div> </div> @endif <!-- Informações de Pagamento --> @if($paymentUrl) <div class="content-section"> <h3>Formas de Pagamento</h3> <div class="payment-info"> <p>Para efetuar o pagamento, utilize um dos métodos abaixo:</p> <div class="payment-buttons"> <a href="{{ $paymentUrl }}" class="btn-primary"> Pagar Agora </a> <a href="{{ route('provider.invoices.show', $invoice) }}" class="btn-secondary"> Ver Fatura </a> </div> </div> </div> @endif <!-- Avisos de Atraso --> @if($invoice->status === 'overdue') <div class="content-section warning-section"> <h3>Atenção: Fatura Vencida</h3> <div class="warning-message"> <p>Esta fatura está vencida há {{ $invoice->due_date->diffInDays(now()) }} dias.</p> <p>Entre em contato conosco para regularizar sua situação.</p> </div> </div> @endif <!-- Observações --> @if($invoice->notes) <div class="content-section"> <h3>Observações</h3> <div class="observations"> {{ $invoice->notes }} </div> </div> @endif </div> </main> <x-email.email-footer :company="$company" />
6. Email Reminder Component
Componente para template de e-mail de lembrete.
Uso Básico
<x-email.email-reminder :type="$type" :entity="$entity" :customer="$customer" :company="$company" />
Parâmetros
| Parâmetro | Tipo | Descrição | Padrão |
|---|---|---|---|
| | Tipo de lembrete (invoice, budget, etc.) | Obrigatório |
| | Entidade do lembrete | Obrigatório |
| | Cliente do lembrete | Obrigatório |
| | Dados da empresa | |
| | URL de ação | |
Estrutura
@props([ 'type', 'entity', 'customer', 'company' => [], 'actionUrl' => null ]) @php $reminderType = match($type) { 'invoice' => 'Fatura', 'budget' => 'Orçamento', 'payment' => 'Pagamento', default => 'Lembrete' }; $reminderMessage = match($type) { 'invoice' => 'Está quase vencendo! Não perca tempo e efetue o pagamento.', 'budget' => 'Está quase expirando! Aproveite enquanto está válido.', 'payment' => 'Lembre-se de efetuar o pagamento para evitar juros.', default => 'Não perca esta oportunidade!' }; @endphp <x-email.email-header :title="'Lembrete: ' . $reminderType . ' #' . $entity->code" :company="$company" /> <!-- Conteúdo Principal --> <main class="email-main"> <div class="email-hero reminder-hero"> <h2 class="hero-title">Lembrete Importante</h2> <p class="hero-subtitle">Olá {{ $customer->display_name }},</p> </div> <div class="email-content"> <div class="content-section"> <h3>{{ $reminderType }} em Destaque</h3> <div class="reminder-content"> <p>{{ $reminderMessage }}</p> <div class="entity-details"> <div class="detail-row"> <span class="detail-label">Número:</span> <span class="detail-value">{{ $entity->code }}</span> </div> <div class="detail-row"> <span class="detail-label">Data:</span> <span class="detail-value">{{ $entity->created_at->format('d/m/Y') }}</span> </div> @if($type === 'invoice' && $entity->due_date) <div class="detail-row"> <span class="detail-label">Vencimento:</span> <span class="detail-value">{{ $entity->due_date->format('d/m/Y') }}</span> </div> @endif <div class="detail-row"> <span class="detail-label">Valor:</span> <span class="detail-value total-amount"> R$ {{ number_format($entity->total, 2, ',', '.') }} </span> </div> </div> </div> </div> <!-- Ações --> @if($actionUrl) <div class="content-section"> <h3>Próximos Passos</h3> <div class="action-buttons"> <a href="{{ $actionUrl }}" class="btn-primary"> {{ $type === 'invoice' ? 'Efetuar Pagamento' : 'Visualizar ' . $reminderType }} </a> <a href="{{ $type === 'invoice' ? route('provider.invoices.index') : route('provider.budgets.index') }}" class="btn-secondary"> Ver Todos </a> </div> </div> @endif <!-- Contato --> <div class="content-section"> <h3>Precisa de Ajuda?</h3> <div class="contact-reminder"> <p>Entre em contato conosco para mais informações:</p> <div class="contact-info"> @if(!empty($company['email'])) <div class="contact-item"> <i class="bi bi-envelope"></i> <a href="mailto:{{ $company['email'] }}">{{ $company['email'] }}</a> </div> @endif @if(!empty($company['phone'])) <div class="contact-item"> <i class="bi bi-telephone"></i> <span>{{ $company['phone'] }}</span> </div> @endif </div> </div> </div> </div> </main> <x-email.email-footer :company="$company" />
7. Email Welcome Component
Componente para template de e-mail de boas-vindas.
Uso Básico
<x-email.email-welcome :user="$user" :company="$company" />
Parâmetros
| Parâmetro | Tipo | Descrição | Padrão |
|---|---|---|---|
| | Usuário recém-cadastrado | Obrigatório |
| | Dados da empresa | |
| | URL de login | |
Estrutura
@props([ 'user', 'company' => [], 'loginUrl' => null ]) <x-email.email-header :title="'Bem-vindo ao ' . ($company['name'] ?? 'Easy Budget')" :company="$company" /> <!-- Conteúdo Principal --> <main class="email-main"> <div class="email-hero welcome-hero"> <h2 class="hero-title">Bem-vindo ao {{ $company['name'] ?? 'Easy Budget' }}!</h2> <p class="hero-subtitle">Olá {{ $user->name }},</p> </div> <div class="email-content"> <div class="content-section"> <h3>Sua Jornada Começa Aqui</h3> <div class="welcome-content"> <p> Seja muito bem-vindo(a) ao {{ $company['name'] ?? 'Easy Budget' }}! Estamos entusiasmados em tê-lo(a) como parte da nossa comunidade. </p> <p> Aqui você encontrará tudo o que precisa para gerenciar seu negócio de forma simples e eficiente. </p> </div> </div> <!-- Benefícios --> <div class="content-section"> <h3>O que você pode fazer</h3> <div class="benefits-list"> <div class="benefit-item"> <i class="bi bi-check-circle"></i> <div class="benefit-content"> <h4>Gestão de Clientes</h4> <p>Cadastre e organize seus clientes de forma simples e rápida.</p> </div> </div> <div class="benefit-item"> <i class="bi bi-check-circle"></i> <div class="benefit-content"> <h4>Emissão de Orçamentos</h4> <p>Crie e envie orçamentos profissionais em minutos.</p> </div> </div> <div class="benefit-item"> <i class="bi bi-check-circle"></i> <div class="benefit-content"> <h4>Controle de Estoque</h4> <p>Gerencie seu estoque e acompanhe movimentações em tempo real.</p> </div> </div> <div class="benefit-item"> <i class="bi bi-check-circle"></i> <div class="benefit-content"> <h4>Relatórios e Analytics</h4> <p>Acompanhe o desempenho do seu negócio com relatórios completos.</p> </div> </div> </div> </div> <!-- Primeiros Passos --> @if($loginUrl) <div class="content-section"> <h3>Primeiros Passos</h3> <div class="steps-list"> <div class="step-item"> <div class="step-number">1</div> <div class="step-content"> <h4>Faça login</h4> <p>Acesse sua conta usando seu e-mail e senha.</p> </div> </div> <div class="step-item"> <div class="step-number">2</div> <div class="step-content"> <h4>Configure seu perfil</h4> <p>Complete seu perfil para personalizar sua experiência.</p> </div> </div> <div class="step-item"> <div class="step-number">3</div> <div class="step-content"> <h4>Comece a usar</h4> <p>Explore as funcionalidades e comece a gerenciar seu negócio.</p> </div> </div> </div> <div class="action-buttons"> <a href="{{ $loginUrl }}" class="btn-primary"> Fazer Login </a> <a href="{{ route('provider.dashboard') }}" class="btn-secondary"> Explorar Sistema </a> </div> </div> @endif <!-- Suporte --> <div class="content-section"> <h3>Precisa de Ajuda?</h3> <div class="support-content"> <p>Estamos aqui para ajudar você a tirar o máximo proveito do nosso sistema.</p> <div class="support-info"> <div class="support-item"> <i class="bi bi-question-circle"></i> <div class="support-content"> <h4>Central de Ajuda</h4> <p>Acesse nossa documentação completa.</p> </div> </div> <div class="support-item"> <i class="bi bi-chat-dots"></i> <div class="support-content"> <h4>Suporte ao Cliente</h4> <p>Entre em contato com nossa equipe.</p> </div> </div> <div class="support-item"> <i class="bi bi-book"></i> <div class="support-content"> <h4>Tutoriais</h4> <p>Assista a vídeos explicativos.</p> </div> </div> </div> </div> </div> </div> </main> <x-email.email-footer :company="$company" />
8. Email Verification Component
Componente para template de e-mail de verificação.
Uso Básico
<x-email.email-verification :user="$user" :verificationUrl="$verificationUrl" :company="$company" />
Parâmetros
| Parâmetro | Tipo | Descrição | Padrão |
|---|---|---|---|
| | Usuário que precisa verificar | Obrigatório |
| | URL de verificação | Obrigatório |
| | Dados da empresa | |
| | Data de expiração do token | |
Estrutura
@props([ 'user', 'verificationUrl', 'company' => [], 'expiresAt' => null ]) <x-email.email-header :title="'Verifique seu E-mail - ' . ($company['name'] ?? 'Easy Budget')" :company="$company" /> <!-- Conteúdo Principal --> <main class="email-main"> <div class="email-hero verification-hero"> <h2 class="hero-title">Verifique seu E-mail</h2> <p class="hero-subtitle">Olá {{ $user->name }},</p> </div> <div class="email-content"> <div class="content-section"> <h3>Confirme seu Endereço de E-mail</h3> <div class="verification-content"> <p> Obrigado por se cadastrar no {{ $company['name'] ?? 'Easy Budget' }}! Para concluir seu cadastro, precisamos verificar seu endereço de e-mail. </p> <p> Clique no botão abaixo para confirmar seu e-mail: </p> </div> </div> <!-- Botão de Verificação --> <div class="content-section"> <div class="verification-action"> <a href="{{ $verificationUrl }}" class="btn-primary verification-btn"> Verificar E-mail </a> </div> </div> <!-- Informações Importantes --> <div class="content-section"> <h3>Informações Importantes</h3> <div class="verification-info"> <ul> <li>Este link expira em {{ $expiresAt ?? '24 horas' }}</li> <li>Se você não se cadastrou no {{ $company['name'] ?? 'Easy Budget' }}, ignore este e-mail</li> <li>Este é um e-mail automático, não é necessário respondê-lo</li> </ul> </div> </div> <!-- Alternativa --> <div class="content-section"> <h3>Problemas com o Botão?</h3> <div class="alternative-info"> <p>Se o botão acima não funcionar, copie e cole este link no seu navegador:</p> <div class="verification-link">{{ $verificationUrl }}</div> </div> </div> <!-- Contato --> <div class="content-section"> <h3>Precisa de Ajuda?</h3> <div class="contact-info"> <p>Se você tiver algum problema com a verificação de e-mail, entre em contato conosco:</p> @if(!empty($company['email'])) <div class="contact-item"> <i class="bi bi-envelope"></i> <a href="mailto:{{ $company['email'] }}">{{ $company['email'] }}</a> </div> @endif </div> </div> </div> </main> <x-email.email-footer :company="$company" />
9. Email Password Component
Componente para template de e-mail de senha.
Uso Básico
<x-email.email-password :type="$type" :user="$user" :resetUrl="$resetUrl" :company="$company" />
Parâmetros
| Parâmetro | Tipo | Descrição | Padrão |
|---|---|---|---|
| | Tipo de e-mail (reset, changed) | Obrigatório |
| | Usuário que solicitou | Obrigatório |
| | URL de redefinição | Obrigatório |
| | Dados da empresa | |
| | Data de expiração do token | |
Estrutura
@props([ 'type', 'user', 'resetUrl', 'company' => [], 'expiresAt' => null ]) @php $emailType = match($type) { 'reset' => 'Redefinição de Senha', 'changed' => 'Senha Alterada', default => 'Alteração de Senha' }; $emailMessage = match($type) { 'reset' => 'Recebemos uma solicitação para redefinir a senha da sua conta.', 'changed' => 'Sua senha foi alterada com sucesso.', default => 'Sua senha foi alterada recentemente.' }; @endphp <x-email.email-header :title="$emailType . ' - ' . ($company['name'] ?? 'Easy Budget')" :company="$company" /> <!-- Conteúdo Principal --> <main class="email-main"> <div class="email-hero password-hero"> <h2 class="hero-title">{{ $emailType }}</h2> <p class="hero-subtitle">Olá {{ $user->name }},</p> </div> <div class="email-content"> <div class="content-section"> <h3>{{ $emailType }}</h3> <div class="password-content"> <p>{{ $emailMessage }}</p> @if($type === 'reset') <p> Para redefinir sua senha, clique no botão abaixo: </p> @endif </div> </div> <!-- Ação --> @if($type === 'reset') <div class="content-section"> <div class="password-action"> <a href="{{ $resetUrl }}" class="btn-primary password-btn"> Redefinir Senha </a> </div> </div> @endif <!-- Informações --> <div class="content-section"> <h3>Informações Importantes</h3> <div class="password-info"> @if($type === 'reset') <ul> <li>Este link expira em {{ $expiresAt ?? '1 hora' }}</li> <li>Se você não solicitou a redefinição de senha, ignore este e-mail</li> <li>Sua senha permanecerá a mesma até que você acesse o link acima</li> </ul> @else <ul> <li>Se você não fez esta alteração, entre em contato conosco imediatamente</li> <li>Recomendamos usar uma senha forte e única</li> <li>Nunca compartilhe sua senha com terceiros</li> </ul> @endif </div> </div> <!-- Segurança --> <div class="content-section"> <h3>Dicas de Segurança</h3> <div class="security-tips"> <ul> <li>Use senhas fortes e únicas</li> <li>Ative a autenticação de dois fatores</li> <li>Nunca compartilhe suas credenciais</li> <li>Altere sua senha periodicamente</li> </ul> </div> </div> <!-- Contato --> <div class="content-section"> <h3>Precisa de Ajuda?</h3> <div class="contact-info"> <p>Se você tiver algum problema ou suspeitar de atividade não autorizada:</p> @if(!empty($company['email'])) <div class="contact-item"> <i class="bi bi-envelope"></i> <a href="mailto:{{ $company['email'] }}">{{ $company['email'] }}</a> </div> @endif </div> </div> </div> </main> <x-email.email-footer :company="$company" />
10. Email Support Component
Componente para template de e-mail de suporte.
Uso Básico
<x-email.email-support :ticket="$ticket" :customer="$customer" :company="$company" />
Parâmetros
| Parâmetro | Tipo | Descrição | Padrão |
|---|---|---|---|
| | Ticket de suporte | Obrigatório |
| | Cliente do ticket | Obrigatório |
| | Dados da empresa | |
| | URL do ticket | |
Estrutura
@props([ 'ticket', 'customer', 'company' => [], 'ticketUrl' => null ]) <x-email.email-header :title="'Ticket de Suporte #' . $ticket->id . ' - ' . $ticket->subject" :company="$company" /> <!-- Conteúdo Principal --> <main class="email-main"> <div class="email-hero support-hero"> <h2 class="hero-title">Ticket de Suporte</h2> <p class="hero-subtitle">Olá {{ $customer->display_name }},</p> </div> <div class="email-content"> <div class="content-section"> <h3>Detalhes do Ticket</h3> <div class="ticket-details"> <div class="detail-row"> <span class="detail-label">Número:</span> <span class="detail-value">#{{ $ticket->id }}</span> </div> <div class="detail-row"> <span class="detail-label">Assunto:</span> <span class="detail-value">{{ $ticket->subject }}</span> </div> <div class="detail-row"> <span class="detail-label">Status:</span> <span class="status-badge status-{{ $ticket->status }}"> {{ ucfirst($ticket->status) }} </span> </div> <div class="detail-row"> <span class="detail-label">Prioridade:</span> <span class="detail-value">{{ ucfirst($ticket->priority) }}</span> </div> <div class="detail-row"> <span class="detail-label">Data:</span> <span class="detail-value">{{ $ticket->created_at->format('d/m/Y H:i') }}</span> </div> </div> </div> <!-- Mensagem --> <div class="content-section"> <h3>Mensagem</h3> <div class="ticket-message"> <p>{{ $ticket->message }}</p> </div> </div> <!-- Resposta --> @if($ticket->response) <div class="content-section"> <h3>Resposta da Equipe</h3> <div class="ticket-response"> <p>{{ $ticket->response }}</p> <div class="response-meta"> <span class="response-date">{{ $ticket->updated_at->format('d/m/Y H:i') }}</span> <span class="response-agent">{{ $ticket->agent?->name ?? 'Equipe de Suporte' }}</span> </div> </div> </div> @endif <!-- Ações --> @if($ticketUrl) <div class="content-section"> <h3>Próximos Passos</h3> <div class="action-buttons"> <a href="{{ $ticketUrl }}" class="btn-primary"> Ver Ticket </a> <a href="{{ route('provider.support.index') }}" class="btn-secondary"> Ver Todos os Tickets </a> </div> </div> @endif <!-- Prazo de Resposta --> <div class="content-section"> <h3>Prazo de Resposta</h3> <div class="response-time"> <p> Responderemos ao seu ticket em até 24 horas úteis. Agradecemos a sua paciência. </p> </div> </div> <!-- Contato Alternativo --> <div class="content-section"> <h3>Contato Alternativo</h3> <div class="alternative-contact"> <p>Para questões urgentes, você também pode entrar em contato:</p> <div class="contact-info"> @if(!empty($company['phone'])) <div class="contact-item"> <i class="bi bi-telephone"></i> <span>{{ $company['phone'] }}</span> </div> @endif @if(!empty($company['email'])) <div class="contact-item"> <i class="bi bi-envelope"></i> <a href="mailto:{{ $company['email'] }}">{{ $company['email'] }}</a> </div> @endif </div> </div> </div> </div> </main> <x-email.email-footer :company="$company" />
11. Email Styles Component
Componente para estilos CSS padrão de e-mails.
Uso Básico
<x-email.email-styles />
Estrutura
<style> /* Reset e Configurações Básicas */ body { font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, Helvetica, Arial, sans-serif; font-size: 16px; line-height: 1.6; color: #333; margin: 0; padding: 0; background-color: #f4f4f4; } .email-container { max-width: 600px; margin: 0 auto; background-color: #ffffff; box-shadow: 0 4px 6px rgba(0, 0, 0, 0.1); } /* Cabeçalho */ .email-header { background: linear-gradient(135deg, #0d6efd 0%, #007bff 100%); color: white; padding: 30px; text-align: center; } .header-content { display: flex; align-items: center; justify-content: center; gap: 20px; } .company-logo img { max-height: 60px; filter: brightness(0) invert(1); } .company-info { text-align: left; } .company-name { font-size: 24px; font-weight: bold; margin: 0 0 5px 0; letter-spacing: 1px; } .company-tagline { font-size: 14px; opacity: 0.9; margin: 0; } /* Conteúdo Principal */ .email-main { padding: 30px; } /* Heróis */ .email-hero { text-align: center; margin-bottom: 30px; padding: 20px; border-radius: 8px; } .hero-title { font-size: 28px; font-weight: bold; margin: 0 0 10px 0; color: #333; } .hero-subtitle { font-size: 18px; color: #666; margin: 0; } .reminder-hero { background-color: #fff3cd; border: 1px solid #ffeaa7; } .welcome-hero { background-color: #d4edda; border: 1px solid #c3e6cb; } .verification-hero { background-color: #cff4fc; border: 1px solid #b6effb; } .password-hero { background-color: #f8d7da; border: 1px solid #f5c6cb; } .support-hero { background-color: #e2e3e5; border: 1px solid #d6d8db; } /* Seções de Conteúdo */ .content-section { margin-bottom: 30px; } .content-section h3 { font-size: 20px; font-weight: bold; color: #333; margin: 0 0 15px 0; border-bottom: 2px solid #0d6efd; padding-bottom: 10px; } .content-section p { margin: 0 0 15px 0; line-height: 1.6; } /* Detalhes */ .budget-details, .invoice-details, .entity-details, .ticket-details { background-color: #f8f9fa; padding: 20px; border-radius: 8px; border: 1px solid #e9ecef; } .detail-row { display: flex; justify-content: space-between; align-items: center; padding: 10px 0; border-bottom: 1px solid #e9ecef; } .detail-row:last-child { border-bottom: none; } .detail-label { font-weight: bold; color: #666; } .detail-value { font-weight: 600; color: #333; } .total-amount { font-size: 20px; color: #0d6efd; } /* Status */ .status-badge { padding: 4px 12px; border-radius: 20px; font-size: 12px; font-weight: bold; text-transform: uppercase; } .status-active, .status-approved, .status-paid { background-color: #d4edda; color: #155724; border: 1px solid #c3e6cb; } .status-inactive, .status-rejected, .status-overdue { background-color: #f8d7da; color: #721c24; border: 1px solid #f5c6cb; } .status-pending, .status-pending-payment { background-color: #fff3cd; color: #856404; border: 1px solid #ffeaa7; } /* Listas */ .services-list, .invoice-items { background-color: #f8f9fa; padding: 15px; border-radius: 8px; border: 1px solid #e9ecef; } .service-item, .item-row { display: flex; justify-content: space-between; align-items: center; padding: 10px 0; border-bottom: 1px solid #e9ecef; } .service-item:last-child, .item-row:last-child { border-bottom: none; } .service-info { flex: 1; } .service-code { font-weight: bold; color: #0d6efd; } .service-description, .item-description { font-size: 14px; color: #666; margin-top: 2px; } .service-price, .item-price, .item-total { font-weight: bold; color: #333; } /* Resumo */ .invoice-summary { background-color: #f8f9fa; padding: 15px; border-radius: 8px; border: 1px solid #e9ecef; margin-top: 15px; } .summary-row { display: flex; justify-content: space-between; padding: 8px 0; border-bottom: 1px solid #e9ecef; } .summary-row:last-child { border-bottom: none; } .summary-label { font-weight: bold; color: #666; } .summary-value { font-weight: bold; color: #333; } .total-row { background-color: #e9ecef; font-size: 18px; } /* Benefícios */ .benefits-list { display: grid; grid-template-columns: 1fr 1fr; gap: 15px; } .benefit-item { display: flex; gap: 15px; padding: 15px; background-color: #f8f9fa; border-radius: 8px; border: 1px solid #e9ecef; } .benefit-item i { font-size: 24px; color: #0d6efd; flex-shrink: 0; } .benefit-content h4 { margin: 0 0 5px 0; font-size: 16px; } .benefit-content p { margin: 0; font-size: 14px; color: #666; } /* Passos */ .steps-list { display: grid; grid-template-columns: repeat(3, 1fr); gap: 20px; } .step-item { display: flex; gap: 15px; padding: 20px; background-color: #f8f9fa; border-radius: 8px; border: 1px solid #e9ecef; } .step-number { width: 40px; height: 40px; background-color: #0d6efd; color: white; border-radius: 50%; display: flex; align-items: center; justify-content: center; font-weight: bold; font-size: 18px; flex-shrink: 0; } .step-content h4 { margin: 0 0 5px 0; font-size: 16px; } .step-content p { margin: 0; font-size: 14px; color: #666; } /* Listas */ .verification-info ul, .password-info ul, .security-tips ul { padding-left: 20px; margin: 15px 0; } .verification-info li, .password-info li, .security-tips li { margin-bottom: 8px; line-height: 1.5; } /* Botões */ .action-buttons, .payment-buttons, .verification-action, .password-action { display: flex; gap: 15px; margin: 20px 0; } .btn-primary, .btn-secondary { display: inline-block; padding: 12px 24px; border-radius: 6px; text-decoration: none; font-weight: bold; text-align: center; transition: all 0.3s ease; } .btn-primary { background-color: #0d6efd; color: white; border: 1px solid #0d6efd; } .btn-primary:hover { background-color: #0b5ed7; border-color: #0b5ed7; transform: translateY(-1px); } .btn-secondary { background-color: #6c757d; color: white; border: 1px solid #6c757d; } .btn-secondary:hover { background-color: #5c636a; border-color: #5c636a; transform: translateY(-1px); } .verification-btn, .password-btn { width: 100%; display: block; text-align: center; } /* Rodapé */ .email-footer { background-color: #f8f9fa; padding: 30px; border-top: 1px solid #e9ecef; } .footer-content { display: grid; grid-template-columns: 1fr 1fr 1fr; gap: 30px; margin-bottom: 20px; } .footer-section h3, .footer-section h4 { margin: 0 0 10px 0; font-size: 16px; color: #333; } .footer-section p { margin: 0 0 10px 0; font-size: 14px; color: #666; line-height: 1.5; } .contact-info { display: flex; flex-direction: column; gap: 8px; } .contact-item { display: flex; align-items: center; gap: 10px; font-size: 14px; color: #666; } .contact-item i { color: #0d6efd; font-size: 16px; } .contact-item a { color: #0d6efd; text-decoration: none; } .contact-item a:hover { text-decoration: underline; } .social-links { display: flex; gap: 10px; } .social-link { display: flex; align-items: center; justify-content: center; width: 36px; height: 36px; background-color: #0d6efd; color: white; border-radius: 50%; text-decoration: none; transition: all 0.3s ease; } .social-link:hover { background-color: #0b5ed7; transform: translateY(-1px); } .footer-bottom { display: flex; justify-content: space-between; align-items: center; border-top: 1px solid #e9ecef; padding-top: 20px; } .footer-text { font-size: 12px; color: #666; } .unsubscribe-link { color: #6c757d; text-decoration: none; font-size: 12px; } .unsubscribe-link:hover { text-decoration: underline; } /* Classes de utilidade */ .warning-section { background-color: #fff3cd; border: 1px solid #ffeaa7; padding: 20px; border-radius: 8px; } .warning-message { background-color: #fff8d6; padding: 15px; border-radius: 6px; border-left: 4px solid #ffc107; } .support-info { display: grid; grid-template-columns: 1fr 1fr 1fr; gap: 15px; } .support-item { display: flex; gap: 15px; padding: 15px; background-color: #f8f9fa; border-radius: 8px; border: 1px solid #e9ecef; } .support-item i { font-size: 24px; color: #0d6efd; flex-shrink: 0; } .support-content h4 { margin: 0 0 5px 0; font-size: 16px; } .support-content p { margin: 0; font-size: 14px; color: #666; } .verification-link { background-color: #f8f9fa; padding: 10px; border-radius: 6px; border: 1px solid #e9ecef; font-family: monospace; font-size: 12px; word-break: break-all; } /* Responsividade */ @media (max-width: 600px) { .email-container { width: 100%; margin: 0; } .header-content { flex-direction: column; text-align: center; } .benefits-list, .steps-list, .support-info { grid-template-columns: 1fr; } .footer-content { grid-template-columns: 1fr; } .action-buttons, .payment-buttons { flex-direction: column; } .detail-row { flex-direction: column; align-items: flex-start; gap: 5px; } .service-item, .item-row { flex-direction: column; align-items: flex-start; gap: 10px; } } </style>
12. Integração com Padrões Existentes
Uso em Mailables
// BudgetNotificationMail.php class BudgetNotificationMail extends Mailable { use Queueable, SerializesModels; public function __construct(public Budget $budget, public Customer $customer) { // } public function build() { $company = [ 'name' => tenant('name'), 'logo' => tenant('logo'), 'email' => tenant('email'), 'phone' => tenant('phone'), 'website' => tenant('website'), ]; return $this->subject("Orçamento #{$this->budget->code} - {$this->customer->display_name}") ->view('components.email.email-budget', [ 'budget' => $this->budget, 'customer' => $this->customer, 'company' => $company, 'actionUrl' => route('provider.budgets.show', $this->budget) ]); } }
Uso em Controllers
// EmailController.php public function previewBudget(Budget $budget) { $customer = $budget->customer; $company = [ 'name' => tenant('name'), 'logo' => tenant('logo'), 'email' => tenant('email'), 'phone' => tenant('phone'), 'website' => tenant('website'), ]; return view('components.email.email-budget', [ 'budget' => $budget, 'customer' => $customer, 'company' => $company, 'actionUrl' => route('provider.budgets.show', $budget) ]); }
Estilos CSS
/* Email Components Styles */ .email-container { max-width: 600px; margin: 0 auto; background-color: #ffffff; } .email-header { background: linear-gradient(135deg, #0d6efd 0%, #007bff 100%); color: white; } .btn-primary { background-color: #0d6efd; color: white; padding: 12px 24px; border-radius: 6px; text-decoration: none; } .status-badge { padding: 4px 12px; border-radius: 20px; font-size: 12px; font-weight: bold; }
13. JavaScript Interatividade
Visualização de E-mails
// email-preview.js document.addEventListener('DOMContentLoaded', function() { // Botões de visualização de e-mail const emailPreviewButtons = document.querySelectorAll('.btn-email-preview'); emailPreviewButtons.forEach(button => { button.addEventListener('click', function() { const url = this.getAttribute('data-url'); if (url) { window.open(url, '_blank', 'width=800,height=600'); } }); }); // Botões de envio de e-mail const emailSendButtons = document.querySelectorAll('.btn-email-send'); emailSendButtons.forEach(button => { button.addEventListener('click', function() { const form = this.closest('form'); if (form) { form.submit(); } }); }); });
14. Validação e Segurança
Autorização
// EmailPolicy.php public function sendBudget(User $user, Budget $budget) { return $user->tenant_id === $budget->tenant_id; } public function sendInvoice(User $user, Invoice $invoice) { return $user->tenant_id === $invoice->tenant_id; } public function sendSupport(User $user, Support $ticket) { return $user->tenant_id === $ticket->tenant_id; }
Validations
{{-- Email Budget com validação de permissões --}} @can('sendBudget', $budget) <x-email.email-budget :budget="$budget" :customer="$customer" :company="$company" /> @endcan {{-- Email Invoice com validação de status --}} @can('sendInvoice', $invoice) <x-email.email-invoice :invoice="$invoice" :customer="$customer" :company="$company" /> @endcan
Este padrão de components para e-mails garante consistência visual, reutilização de código e manutenibilidade, seguindo os mesmos princípios estabelecidos nos outros components do sistema.