## New Features - Email notifications for overdue and upcoming payments - User preferences page for notification settings - Daily scheduler to send alerts at user-configured time - Smart analysis: payable items, transfer suggestions between accounts ## Backend - Migration for user_preferences table - SendDuePaymentsAlert Artisan command - DuePaymentsAlert Mailable with HTML/text templates - UserPreferenceController with test-notification endpoint - Scheduler config for notify:due-payments command ## Frontend - Preferences.jsx page with notification toggle - API service for preferences - Route and menu link for settings - Translations (PT-BR, EN, ES) ## Server - Cron configured for Laravel scheduler Version: 1.44.5
376 lines
13 KiB
PHP
376 lines
13 KiB
PHP
<!DOCTYPE html>
|
|
<html lang="pt-BR">
|
|
<head>
|
|
<meta charset="UTF-8">
|
|
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
|
<title>Alerta de Pagamentos</title>
|
|
<style>
|
|
body {
|
|
font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, Helvetica, Arial, sans-serif;
|
|
line-height: 1.6;
|
|
color: #333;
|
|
max-width: 600px;
|
|
margin: 0 auto;
|
|
padding: 20px;
|
|
background-color: #f5f5f5;
|
|
}
|
|
.container {
|
|
background-color: #ffffff;
|
|
border-radius: 8px;
|
|
padding: 30px;
|
|
box-shadow: 0 2px 4px rgba(0,0,0,0.1);
|
|
}
|
|
.header {
|
|
text-align: center;
|
|
border-bottom: 2px solid #0f172a;
|
|
padding-bottom: 20px;
|
|
margin-bottom: 20px;
|
|
}
|
|
.header h1 {
|
|
color: #0f172a;
|
|
margin: 0;
|
|
font-size: 24px;
|
|
}
|
|
.summary-box {
|
|
background: linear-gradient(135deg, #1e293b 0%, #0f172a 100%);
|
|
color: white;
|
|
padding: 20px;
|
|
border-radius: 8px;
|
|
margin: 20px 0;
|
|
}
|
|
.summary-row {
|
|
display: flex;
|
|
justify-content: space-between;
|
|
padding: 8px 0;
|
|
border-bottom: 1px solid rgba(255,255,255,0.1);
|
|
}
|
|
.summary-row:last-child {
|
|
border-bottom: none;
|
|
}
|
|
.summary-label {
|
|
color: #94a3b8;
|
|
}
|
|
.summary-value {
|
|
font-weight: bold;
|
|
}
|
|
.shortage {
|
|
background-color: #dc2626;
|
|
color: white;
|
|
padding: 15px;
|
|
border-radius: 8px;
|
|
text-align: center;
|
|
margin: 20px 0;
|
|
}
|
|
.shortage h3 {
|
|
margin: 0 0 5px 0;
|
|
}
|
|
.shortage .amount {
|
|
font-size: 28px;
|
|
font-weight: bold;
|
|
}
|
|
.section {
|
|
margin: 25px 0;
|
|
}
|
|
.section-title {
|
|
font-size: 18px;
|
|
color: #0f172a;
|
|
border-bottom: 2px solid #e2e8f0;
|
|
padding-bottom: 10px;
|
|
margin-bottom: 15px;
|
|
}
|
|
.item {
|
|
background-color: #f8fafc;
|
|
border-left: 4px solid #64748b;
|
|
padding: 12px 15px;
|
|
margin: 10px 0;
|
|
border-radius: 0 4px 4px 0;
|
|
}
|
|
.item.overdue {
|
|
border-left-color: #dc2626;
|
|
background-color: #fef2f2;
|
|
}
|
|
.item.tomorrow {
|
|
border-left-color: #f59e0b;
|
|
background-color: #fffbeb;
|
|
}
|
|
.item.payable {
|
|
border-left-color: #22c55e;
|
|
background-color: #f0fdf4;
|
|
}
|
|
.item.unpayable {
|
|
border-left-color: #dc2626;
|
|
background-color: #fef2f2;
|
|
}
|
|
.item-header {
|
|
display: flex;
|
|
justify-content: space-between;
|
|
align-items: center;
|
|
}
|
|
.item-description {
|
|
font-weight: 600;
|
|
color: #1e293b;
|
|
}
|
|
.item-amount {
|
|
font-weight: bold;
|
|
color: #dc2626;
|
|
}
|
|
.item-details {
|
|
font-size: 13px;
|
|
color: #64748b;
|
|
margin-top: 5px;
|
|
}
|
|
.badge {
|
|
display: inline-block;
|
|
padding: 2px 8px;
|
|
border-radius: 12px;
|
|
font-size: 11px;
|
|
font-weight: bold;
|
|
text-transform: uppercase;
|
|
}
|
|
.badge-overdue {
|
|
background-color: #dc2626;
|
|
color: white;
|
|
}
|
|
.badge-tomorrow {
|
|
background-color: #f59e0b;
|
|
color: white;
|
|
}
|
|
.badge-ok {
|
|
background-color: #22c55e;
|
|
color: white;
|
|
}
|
|
.account-balance {
|
|
display: flex;
|
|
justify-content: space-between;
|
|
padding: 10px 15px;
|
|
background-color: #f8fafc;
|
|
margin: 5px 0;
|
|
border-radius: 4px;
|
|
}
|
|
.account-name {
|
|
font-weight: 500;
|
|
}
|
|
.balance-positive {
|
|
color: #22c55e;
|
|
font-weight: bold;
|
|
}
|
|
.balance-negative {
|
|
color: #dc2626;
|
|
font-weight: bold;
|
|
}
|
|
.transfer-suggestion {
|
|
background-color: #eff6ff;
|
|
border: 1px solid #3b82f6;
|
|
border-radius: 8px;
|
|
padding: 15px;
|
|
margin: 10px 0;
|
|
}
|
|
.transfer-arrow {
|
|
text-align: center;
|
|
font-size: 20px;
|
|
color: #3b82f6;
|
|
}
|
|
.footer {
|
|
text-align: center;
|
|
margin-top: 30px;
|
|
padding-top: 20px;
|
|
border-top: 1px solid #e2e8f0;
|
|
color: #64748b;
|
|
font-size: 13px;
|
|
}
|
|
.cta-button {
|
|
display: inline-block;
|
|
background-color: #3b82f6;
|
|
color: white;
|
|
padding: 12px 24px;
|
|
text-decoration: none;
|
|
border-radius: 6px;
|
|
font-weight: bold;
|
|
margin: 20px 0;
|
|
}
|
|
.cta-button:hover {
|
|
background-color: #2563eb;
|
|
}
|
|
</style>
|
|
</head>
|
|
<body>
|
|
<div class="container">
|
|
<div class="header">
|
|
<h1>💰 WEBMoney - Alerta de Pagamentos</h1>
|
|
<p>Olá, {{ $userName }}!</p>
|
|
</div>
|
|
|
|
<!-- Summary Box -->
|
|
<div class="summary-box">
|
|
<div class="summary-row">
|
|
<span class="summary-label">💳 Saldo Total Disponível</span>
|
|
<span class="summary-value">{{ number_format($totalAvailable, 2, ',', '.') }} {{ $currency }}</span>
|
|
</div>
|
|
<div class="summary-row">
|
|
<span class="summary-label">📋 Total a Pagar</span>
|
|
<span class="summary-value">{{ number_format($totalDue, 2, ',', '.') }} {{ $currency }}</span>
|
|
</div>
|
|
@if($shortage > 0)
|
|
<div class="summary-row" style="color: #fca5a5;">
|
|
<span class="summary-label">⚠️ Falta</span>
|
|
<span class="summary-value">{{ number_format($shortage, 2, ',', '.') }} {{ $currency }}</span>
|
|
</div>
|
|
@else
|
|
<div class="summary-row" style="color: #86efac;">
|
|
<span class="summary-label">✅ Situação</span>
|
|
<span class="summary-value">Saldo suficiente!</span>
|
|
</div>
|
|
@endif
|
|
</div>
|
|
|
|
@if($shortage > 0)
|
|
<div class="shortage">
|
|
<h3>⚠️ SALDO INSUFICIENTE</h3>
|
|
<div class="amount">-{{ number_format($shortage, 2, ',', '.') }} {{ $currency }}</div>
|
|
<p style="margin: 10px 0 0 0; font-size: 14px;">Você não tem saldo suficiente para cobrir todos os pagamentos.</p>
|
|
</div>
|
|
@endif
|
|
|
|
<!-- Account Balances -->
|
|
<div class="section">
|
|
<h2 class="section-title">💳 Saldo das Contas</h2>
|
|
@foreach($accountBalances as $account)
|
|
<div class="account-balance">
|
|
<span class="account-name">{{ $account['name'] }}</span>
|
|
<span class="{{ $account['balance'] >= 0 ? 'balance-positive' : 'balance-negative' }}">
|
|
{{ number_format($account['balance'], 2, ',', '.') }} {{ $account['currency'] }}
|
|
</span>
|
|
</div>
|
|
@endforeach
|
|
</div>
|
|
|
|
<!-- Overdue Items -->
|
|
@if(count($overdueItems) > 0)
|
|
<div class="section">
|
|
<h2 class="section-title">🔴 Pagamentos Vencidos ({{ count($overdueItems) }})</h2>
|
|
@foreach($overdueItems as $item)
|
|
<div class="item overdue">
|
|
<div class="item-header">
|
|
<span class="item-description">{{ $item['description'] }}</span>
|
|
<span class="item-amount">{{ number_format($item['amount'], 2, ',', '.') }} {{ $item['currency'] }}</span>
|
|
</div>
|
|
<div class="item-details">
|
|
<span class="badge badge-overdue">{{ $item['days_overdue'] }} dias de atraso</span>
|
|
• Venceu em {{ \Carbon\Carbon::parse($item['due_date'])->format('d/m/Y') }}
|
|
@if($item['account_name'])
|
|
• Conta: {{ $item['account_name'] }}
|
|
@endif
|
|
</div>
|
|
</div>
|
|
@endforeach
|
|
</div>
|
|
@endif
|
|
|
|
<!-- Tomorrow Items -->
|
|
@if(count($tomorrowItems) > 0)
|
|
<div class="section">
|
|
<h2 class="section-title">🟡 Vencem Amanhã ({{ count($tomorrowItems) }})</h2>
|
|
@foreach($tomorrowItems as $item)
|
|
<div class="item tomorrow">
|
|
<div class="item-header">
|
|
<span class="item-description">{{ $item['description'] }}</span>
|
|
<span class="item-amount">{{ number_format($item['amount'], 2, ',', '.') }} {{ $item['currency'] }}</span>
|
|
</div>
|
|
<div class="item-details">
|
|
<span class="badge badge-tomorrow">Amanhã</span>
|
|
• {{ \Carbon\Carbon::parse($item['due_date'])->format('d/m/Y') }}
|
|
@if($item['account_name'])
|
|
• Conta: {{ $item['account_name'] }}
|
|
@endif
|
|
</div>
|
|
</div>
|
|
@endforeach
|
|
</div>
|
|
@endif
|
|
|
|
<!-- Payable Items -->
|
|
@if(count($payableItems) > 0)
|
|
<div class="section">
|
|
<h2 class="section-title">✅ Pagamentos Possíveis ({{ count($payableItems) }})</h2>
|
|
<p style="color: #64748b; font-size: 14px;">Com base no saldo atual, você consegue pagar:</p>
|
|
@foreach($payableItems as $item)
|
|
<div class="item payable">
|
|
<div class="item-header">
|
|
<span class="item-description">{{ $item['description'] }}</span>
|
|
<span class="item-amount" style="color: #22c55e;">{{ number_format($item['amount'], 2, ',', '.') }} {{ $item['currency'] }}</span>
|
|
</div>
|
|
<div class="item-details">
|
|
<span class="badge badge-ok">✓ Pode pagar</span>
|
|
@if($item['account_name'])
|
|
• Conta: {{ $item['account_name'] }}
|
|
@endif
|
|
</div>
|
|
</div>
|
|
@endforeach
|
|
</div>
|
|
@endif
|
|
|
|
<!-- Unpayable Items -->
|
|
@if(count($unpayableItems) > 0)
|
|
<div class="section">
|
|
<h2 class="section-title">❌ Sem Saldo Suficiente ({{ count($unpayableItems) }})</h2>
|
|
<p style="color: #64748b; font-size: 14px;">Não há saldo disponível para estes pagamentos:</p>
|
|
@foreach($unpayableItems as $item)
|
|
<div class="item unpayable">
|
|
<div class="item-header">
|
|
<span class="item-description">{{ $item['description'] }}</span>
|
|
<span class="item-amount">{{ number_format($item['amount'], 2, ',', '.') }} {{ $item['currency'] }}</span>
|
|
</div>
|
|
<div class="item-details">
|
|
<span class="badge badge-overdue">✗ Sem saldo</span>
|
|
@if($item['account_name'])
|
|
• Conta: {{ $item['account_name'] }}
|
|
@endif
|
|
</div>
|
|
</div>
|
|
@endforeach
|
|
</div>
|
|
@endif
|
|
|
|
<!-- Transfer Suggestions -->
|
|
@if(count($transferSuggestions) > 0)
|
|
<div class="section">
|
|
<h2 class="section-title">💱 Sugestões de Transferência</h2>
|
|
<p style="color: #64748b; font-size: 14px;">Para cobrir os pagamentos, considere transferir entre suas contas:</p>
|
|
@foreach($transferSuggestions as $transfer)
|
|
<div class="transfer-suggestion">
|
|
<div style="display: flex; align-items: center; justify-content: space-between;">
|
|
<div>
|
|
<strong>{{ $transfer['from_account'] }}</strong>
|
|
<div style="font-size: 12px; color: #64748b;">Origem</div>
|
|
</div>
|
|
<div class="transfer-arrow">→</div>
|
|
<div style="text-align: right;">
|
|
<strong>{{ $transfer['to_account'] }}</strong>
|
|
<div style="font-size: 12px; color: #64748b;">Destino</div>
|
|
</div>
|
|
</div>
|
|
<div style="text-align: center; margin-top: 10px; font-size: 20px; font-weight: bold; color: #3b82f6;">
|
|
{{ number_format($transfer['amount'], 2, ',', '.') }} {{ $currency }}
|
|
</div>
|
|
<div style="text-align: center; font-size: 12px; color: #64748b;">{{ $transfer['reason'] }}</div>
|
|
</div>
|
|
@endforeach
|
|
</div>
|
|
@endif
|
|
|
|
<div style="text-align: center;">
|
|
<a href="https://webmoney.cnxifly.com/transactions" class="cta-button">
|
|
Acessar WEBMoney
|
|
</a>
|
|
</div>
|
|
|
|
<div class="footer">
|
|
<p>Este email foi enviado automaticamente pelo sistema WEBMoney.</p>
|
|
<p>Para desativar estas notificações, acesse <a href="https://webmoney.cnxifly.com/preferences">Preferências</a>.</p>
|
|
<p style="margin-top: 15px;">© {{ date('Y') }} WEBMoney - ConneXiFly</p>
|
|
</div>
|
|
</div>
|
|
</body>
|
|
</html>
|