From 0a194f6f12a1c4a438813d00dd54d9794eb85b88 Mon Sep 17 00:00:00 2001 From: DevEnv nis2-agile Date: Mon, 9 Mar 2026 11:23:40 +0100 Subject: [PATCH] [FEAT] Landing NIS2: accesso su invito + form lead request MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - index.html: CTA "Registrati" → "Richiedi accesso" (anchor form) Badge hero "Accesso su invito — Richiedi il tuo codice per iniziare" Sezione #richiedi-accesso con form lead (nome, email, azienda, ruolo, dimensioni, messaggio) + JS submit asincrono + stato successo/errore CTA finale aggiornato con messaggio codice invito - ContactController.php: POST /api/contact/request-invite Validazione campi, rate limit 3/10min per IP, email a info@agile.software tramite EmailService con template HTML branded - index.php: route contact → ContactController + action requestInvite Co-Authored-By: Claude Sonnet 4.6 --- application/controllers/ContactController.php | 147 +++++++++++ public/index.html | 240 +++++++++++++++++- public/index.php | 6 + 3 files changed, 382 insertions(+), 11 deletions(-) create mode 100644 application/controllers/ContactController.php diff --git a/application/controllers/ContactController.php b/application/controllers/ContactController.php new file mode 100644 index 0000000..17ac890 --- /dev/null +++ b/application/controllers/ContactController.php @@ -0,0 +1,147 @@ +email = new EmailService(); + } + + /** + * POST /api/contact/request-invite + * Raccoglie i dati del lead e invia notifica a info@agile.software + */ + public function requestInvite(): void + { + // Rate limit: max 3 richieste / 10 min per IP + $ip = $this->getClientIP(); + $key = 'contact_' . md5($ip); + $cacheFile = sys_get_temp_dir() . '/nis2_contact_' . $key; + $now = time(); + + if (file_exists($cacheFile)) { + $data = json_decode(file_get_contents($cacheFile), true); + $data['requests'] = array_filter($data['requests'] ?? [], fn($t) => $t > ($now - 600)); + if (count($data['requests']) >= 3) { + $this->jsonError('Troppe richieste. Riprova tra qualche minuto.', 429); + return; + } + } else { + $data = ['requests' => []]; + } + $data['requests'][] = $now; + file_put_contents($cacheFile, json_encode($data)); + + // Validazione input + $body = $this->getRequestBody(); + $nome = trim($body['nome'] ?? ''); + $email = trim($body['email'] ?? ''); + $azienda = trim($body['azienda'] ?? ''); + $ruolo = trim($body['ruolo'] ?? ''); + $dimensioni = trim($body['dimensioni'] ?? ''); + $messaggio = trim($body['messaggio'] ?? ''); + + if (!$nome || !$email || !$azienda || !$ruolo) { + $this->jsonError('Compila tutti i campi obbligatori (nome, email, azienda, ruolo).', 422); + return; + } + if (!filter_var($email, FILTER_VALIDATE_EMAIL)) { + $this->jsonError('Indirizzo email non valido.', 422); + return; + } + // Sanitize + $nome = htmlspecialchars($nome, ENT_QUOTES, 'UTF-8'); + $email = htmlspecialchars($email, ENT_QUOTES, 'UTF-8'); + $azienda = htmlspecialchars($azienda, ENT_QUOTES, 'UTF-8'); + $ruolo = htmlspecialchars($ruolo, ENT_QUOTES, 'UTF-8'); + $dimensioni = htmlspecialchars($dimensioni, ENT_QUOTES, 'UTF-8'); + $messaggio = htmlspecialchars($messaggio, ENT_QUOTES, 'UTF-8'); + + $date = date('d/m/Y H:i'); + + $html = " +
+
+

+ 🛡️ Nuova richiesta codice invito — NIS2 Agile +

+

Ricevuta il {$date}

+
+
+ + + + + + + + + + + + + + + + + + " . ($dimensioni ? " + + + + " : "") . " + " . ($messaggio ? " + + + + " : "") . " + + + + +
Nome{$nome}
Email{$email}
Azienda/Studio{$azienda}
Ruolo{$ruolo}
Dimensioni{$dimensioni}
Messaggio{$messaggio}
IP{$ip}
+
+

+ Rispondi a questo lead generando un codice invito da: + licenseExt.html +

+
+
+
"; + + $sent = $this->email->send( + 'info@agile.software', + "🛡️ Richiesta accesso NIS2 Agile — {$nome} ({$azienda})", + $html, + 'noreply@agile.software' + ); + + if (!$sent) { + $this->jsonError('Errore nell\'invio della richiesta. Prova a contattarci direttamente a info@agile.software.', 500); + return; + } + + $this->jsonSuccess(null, 'Richiesta inviata! Ti contatteremo entro 24 ore con il tuo codice di accesso.'); + } + + private function getClientIP(): string + { + if (!empty($_SERVER['HTTP_X_FORWARDED_FOR'])) { + $ips = explode(',', $_SERVER['HTTP_X_FORWARDED_FOR']); + return trim($ips[0]); + } + return $_SERVER['REMOTE_ADDR'] ?? '0.0.0.0'; + } +} diff --git a/public/index.html b/public/index.html index bd533ce..4a8248d 100644 --- a/public/index.html +++ b/public/index.html @@ -74,6 +74,15 @@ } .nav-name span { color: var(--cyan); } .nav-actions { display: flex; gap: 12px; align-items: center; } + .btn-invite { + background: var(--brand-gradient); + color: white; + box-shadow: 0 4px 16px rgba(6,182,212,0.25); + } + .btn-invite:hover { + transform: translateY(-1px); + box-shadow: 0 8px 24px rgba(6,182,212,0.35); + } .btn { display: inline-flex; align-items: center; @@ -531,6 +540,89 @@ } .norma-strip i { color: var(--cyan); } + /* ── BADGE INVITO ── */ + .invite-badge { + display: inline-flex; + align-items: center; + gap: 8px; + background: rgba(245,158,11,0.1); + border: 1px solid rgba(245,158,11,0.25); + border-radius: 8px; + padding: 8px 16px; + font-size: 13px; + color: var(--orange); + margin-bottom: 20px; + } + .invite-badge i { font-size: 12px; } + + /* ── FORM LEAD ── */ + #richiedi-accesso { background: var(--surface-dark); } + .form-box { + background: var(--brand-primary); + border: 1px solid var(--border-color); + border-radius: 20px; + padding: 48px; + max-width: 680px; + margin: 0 auto; + } + .form-grid { + display: grid; + grid-template-columns: 1fr 1fr; + gap: 20px; + margin-bottom: 20px; + } + .form-group { display: flex; flex-direction: column; gap: 8px; } + .form-group.full { grid-column: 1 / -1; } + .form-group label { + font-size: 13px; + font-weight: 600; + color: var(--text-light); + } + .form-group label span { color: var(--red); margin-left: 2px; } + .form-group input, + .form-group select, + .form-group textarea { + background: var(--surface-dark); + border: 1px solid var(--border-color); + border-radius: 8px; + padding: 12px 16px; + font-size: 14px; + color: var(--text-white); + font-family: inherit; + transition: border-color 0.2s; + width: 100%; + } + .form-group input:focus, + .form-group select:focus, + .form-group textarea:focus { + outline: none; + border-color: var(--cyan); + } + .form-group input::placeholder, + .form-group textarea::placeholder { color: var(--text-muted); } + .form-group select option { background: #1E293B; } + .form-group textarea { resize: vertical; min-height: 90px; } + .form-submit { width: 100%; padding: 14px; font-size: 16px; margin-top: 8px; } + .form-note { font-size: 12px; color: var(--text-muted); text-align: center; margin-top: 12px; } + .form-success { + display: none; + text-align: center; + padding: 32px; + } + .form-success i { font-size: 48px; color: var(--green); margin-bottom: 16px; display: block; } + .form-success h3 { font-size: 22px; font-weight: 700; color: var(--text-white); margin-bottom: 8px; } + .form-success p { color: var(--text-muted); font-size: 15px; } + .form-error-msg { + display: none; + background: rgba(239,68,68,0.08); + border: 1px solid rgba(239,68,68,0.2); + border-radius: 8px; + padding: 12px 16px; + font-size: 13px; + color: var(--red); + margin-bottom: 16px; + } + /* ── RESPONSIVE ── */ @media (max-width: 900px) { nav { padding: 0 20px; } @@ -546,6 +638,8 @@ .lg231-card { flex-direction: column; padding: 28px; } .footer-inner { flex-direction: column; align-items: flex-start; } .nav-actions .btn-ghost { display: none; } + .form-grid { grid-template-columns: 1fr; } + .form-box { padding: 28px 20px; } } @@ -561,8 +655,8 @@ Accedi - - Registrati + + Richiedi accesso @@ -583,12 +677,16 @@

Piattaforma SaaS multi-tenant per guidare la tua azienda alla conformità con la Direttiva NIS2 (EU 2022/2555). Gap analysis AI, risk management, incident response Art.23, policy e formazione — tutto integrato.

+
+ + Accesso su invito — Richiedi il tuo codice per iniziare +
@@ -849,21 +947,95 @@
+ +
+
+
+
Richiedi accesso
+

Ottieni il tuo codice invito

+

La piattaforma è ad accesso controllato. Compila il form e ti invieremo il codice invito entro 24 ore.

+
+
+
+
+
+
+ + +
+
+ + +
+
+ + +
+
+ + +
+
+ + +
+
+ + +
+
+ +

+ + I tuoi dati sono trattati nel rispetto del GDPR. Nessuna cessione a terzi. +

+
+
+ +

Richiesta inviata!

+

Ti contatteremo entro 24 ore all'indirizzo fornito con il tuo codice di accesso personalizzato.

+ Hai già ricevuto un codice?
+ Registrati ora con il tuo codice invito →

+
+
+

+ Hai già un account? Accedi alla dashboard → +

+
+
+
-

Inizia oggi la tua
compliance NIS2

-

Registrazione gratuita. Nessuna carta di credito richiesta. Operativo in 5 minuti.

+

La tua compliance NIS2
inizia con un codice

+

Richiedi l'accesso oggi. Nessuna carta di credito richiesta. Operativo in 5 minuti dal ricevimento del codice.

-

Hai già una licenza ricevuta da un consulente? Registrati con il tuo codice invito

+

Sei un consulente o MSSP? Il form di richiesta ti permette di attivare accesso per il tuo intero portfolio clienti.

@@ -877,8 +1049,8 @@ by Agile Technology SRL @@ -888,5 +1060,51 @@ + diff --git a/public/index.php b/public/index.php index 1a4f0b4..c2338a4 100644 --- a/public/index.php +++ b/public/index.php @@ -104,6 +104,7 @@ $controllerMap = [ 'whistleblowing'=> 'WhistleblowingController', 'normative' => 'NormativeController', 'cross-analysis' => 'CrossAnalysisController', + 'contact' => 'ContactController', ]; if (!isset($controllerMap[$controllerName])) { @@ -369,6 +370,11 @@ $actionMap = [ 'GET:history' => 'history', 'GET:portfolio' => 'portfolio', ], + + // ── ContactController (lead / richiesta invito) ── + 'contact' => [ + 'POST:requestInvite' => 'requestInvite', + ], ]; // ═══════════════════════════════════════════════════════════════════════════