Skip to content

Commit 8b4cd70

Browse files
committed
feat: Add Google Gemini API key configuration, enhance session lifetime management with fallback options, and improve gamification insights by integrating new content resolution methods for better user engagement.
1 parent bd28401 commit 8b4cd70

47 files changed

Lines changed: 2031 additions & 493 deletions

File tree

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

.env.example

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -83,3 +83,7 @@ VITE_APP_NAME="${APP_NAME}"
8383

8484
# Jules AI (Keep empty in example, set in .env or GitHub Secrets)
8585
JULES_API_KEY=
86+
87+
# Google Gemini API (Vertex Bot - Admin > Configurações > IA)
88+
# Fallback when not set in Panel; get key at https://aistudio.google.com/app/apikey
89+
GEMINI_API_KEY=

Config.md

Lines changed: 41 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,41 @@
1+
@.cursorrules
2+
@Modules/Gamification
3+
@Modules/Core/Services/FinancialHealthService.php
4+
@Modules/Core/app/Services/SettingService.php
5+
@Modules/Gamification/resources/views/components/vertex-bot.blade.php
6+
7+
**Role:** Senior AI Engineer & UX Master.
8+
**Goal:** Replace the static insight system with the Google Gemini AI API to provide real-time, hyper-personalized financial coaching. Also, upgrade the Bot's visual design to a Premium "Fintech Elite" style.
9+
10+
---
11+
12+
### Phase 1: Gemini AI Integration (The New Brain)
13+
1. **API Setup:** Configure o cliente Guzzle para conectar à API do Google Gemini (utilize uma chave `GEMINI_API_KEY` no `.env`).
14+
2. **Context Provider:** No `GamificationService`, crie um método que gera um "Prompt Contextual":
15+
* Inclua: Percentuais 50/30/20 reais, Score Financeiro, e títulos dos últimos 3 posts do Blog.
16+
* **Segurança LGPD:** Use os helpers existentes para NUNCA enviar nomes reais ou CPFs para a API. Envie apenas métricas e números.
17+
3. **Logic:** Se a API falhar ou estiver sem internet, o sistema deve fazer fallback AUTOMÁTICO para as dicas locais da `insights_bank`.
18+
19+
### Phase 2: High-End UI Redesign (Visual Iron Man)
20+
1. **Component Update:** Refatore o `vertex-bot.blade.php`.
21+
* **Visual:** O balão deve ser maior, com `backdrop-blur-xl`, bordas arredondadas suaves e tipografia Inter 14px.
22+
* **Animations:** Use Tailwind v4 para criar uma entrada "Spring" (suave). Adicione um efeito de "pulsação de luz" no robô quando ele estiver "analisando dados".
23+
2. **Readability:** Garanta que o texto tenha contraste alto (Texto branco em fundo Slate-950/80).
24+
25+
### Phase 3: Strategic Behavior (Anti-Irritation)
26+
1. **Cooldown:** Implemente uma trava. O robô só aparece:
27+
* No primeiro login do dia.
28+
* OU se o usuário realizar uma transação que mude drasticamente um pilar do 50/30/20.
29+
2. **Typewriter Effect:** Adicione um efeito de "digitando" no balão para simular a resposta da IA em tempo real.
30+
31+
---
32+
33+
### Phase 4: Pro-Only Features
34+
1. **Diferenciação:** Para usuários FREE, o Gemini dá dicas curtas. Para usuários PRO, o Gemini faz uma análise profunda cruzando os gastos com os artigos do Blog.
35+
36+
**Technical Constraints:**
37+
* Use FontAwesome 7.1 Pro Duotone.
38+
* Mantenha o processamento assíncrono para não travar o carregamento da página.
39+
* O robô deve sempre se identificar como "Vertex Bot - Seu Mentor de Elite".
40+
41+
**Execute Phase 1 (Gemini Connection) and Phase 2 (New Design) first.**
Lines changed: 72 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,72 @@
1+
<?php
2+
3+
declare(strict_types=1);
4+
5+
namespace Modules\Core\Http\Middleware;
6+
7+
use App\Models\SupportAuditLog;
8+
use Closure;
9+
use Illuminate\Http\Request;
10+
use Illuminate\Support\Facades\Auth;
11+
use Modules\Core\Models\Inspection;
12+
use Symfony\Component\HttpFoundation\Response;
13+
14+
class EnsureInspectionNotExpired
15+
{
16+
/**
17+
* Handle an incoming request. Auto-stops inspection if max duration exceeded.
18+
*/
19+
public function handle(Request $request, Closure $next): Response
20+
{
21+
$inspectionId = session('impersonate_inspection_id');
22+
$originalAgentId = session('original_agent_id');
23+
24+
if (! $inspectionId || ! $originalAgentId) {
25+
return $next($request);
26+
}
27+
28+
$inspection = Inspection::find($inspectionId);
29+
if (! $inspection || $inspection->status !== 'active') {
30+
return $next($request);
31+
}
32+
33+
$maxDuration = (int) setting('security_inspection_max_duration', 1800);
34+
$startedAt = $inspection->started_at;
35+
36+
if (! $startedAt || $startedAt->diffInSeconds(now()) < $maxDuration) {
37+
return $next($request);
38+
}
39+
40+
$inspection->update([
41+
'status' => 'completed',
42+
'ended_at' => now(),
43+
]);
44+
45+
session()->forget('impersonate_inspection_id');
46+
session()->forget('original_agent_id');
47+
48+
Auth::logout();
49+
Auth::loginUsingId($originalAgentId);
50+
51+
SupportAuditLog::create([
52+
'agent_id' => $originalAgentId,
53+
'user_id' => $inspection->user_id,
54+
'action' => 'inspection_expired',
55+
'metadata' => [
56+
'report' => 'Sessão expirada automaticamente por tempo máximo configurado.',
57+
'duration_seconds' => $startedAt->diffInSeconds(now()),
58+
'ticket_id' => $inspection->ticket_id,
59+
'conversation_id' => $inspection->conversation_id,
60+
],
61+
'ip_address' => $request->ip(),
62+
]);
63+
64+
if ($inspection->conversation_id) {
65+
return redirect()->route('support.chat.show', $inspection->conversation)
66+
->with('warning', 'Inspeção encerrada automaticamente por tempo limite.');
67+
}
68+
69+
return redirect()->route('support.tickets.show', $inspection->ticket_id)
70+
->with('warning', 'Inspeção encerrada automaticamente por tempo limite.');
71+
}
72+
}
Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,37 @@
1+
<?php
2+
3+
declare(strict_types=1);
4+
5+
namespace Modules\Core\Http\Middleware;
6+
7+
use Closure;
8+
use Illuminate\Http\Request;
9+
use Illuminate\Support\Facades\Cache;
10+
use Illuminate\Support\Facades\Schema;
11+
use Symfony\Component\HttpFoundation\Response;
12+
13+
class EnsureNotLockedOut
14+
{
15+
/**
16+
* Handle an incoming request. Redirects if IP is locked out after too many failed logins.
17+
*/
18+
public function handle(Request $request, Closure $next): Response
19+
{
20+
if (! Schema::hasTable('settings')) {
21+
return $next($request);
22+
}
23+
24+
$ip = $request->ip();
25+
$lockoutKey = 'login_lockout_' . $ip;
26+
27+
if (Cache::has($lockoutKey)) {
28+
$lockoutMinutes = (int) setting('security_lockout_time', 15);
29+
30+
return redirect()->route('login')
31+
->withErrors(['throttle' => "Muitas tentativas de login. Aguarde {$lockoutMinutes} minutos e tente novamente."])
32+
->onlyInput('email');
33+
}
34+
35+
return $next($request);
36+
}
37+
}

Modules/Core/app/Providers/CoreServiceProvider.php

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -127,8 +127,8 @@ protected function overrideConfigsFromDatabase(): void
127127
]);
128128
}
129129

130-
// Override session lifetime from security settings
131-
$sessionLifetime = (int) $settings->get('session_lifetime', config('session.lifetime', 120));
130+
// Override session lifetime from security settings (fallback for migration)
131+
$sessionLifetime = (int) ($settings->get('security_session_lifetime') ?? $settings->get('session_lifetime') ?? config('session.lifetime', 120));
132132
if ($sessionLifetime > 0) {
133133
config(['session.lifetime' => $sessionLifetime]);
134134
}

0 commit comments

Comments
 (0)