Initialize Next.js project with essential configurations and components
- Added .gitignore to exclude unnecessary files and directories. - Created next.config.js for Next.js configuration. - Set up package.json and package-lock.json with dependencies including Next.js, React, and TypeScript. - Implemented Tailwind CSS for styling with a dedicated tailwind.config.ts. - Developed core application structure including layout, pages, and components for header, footer, and various sections (Hero, Services, Pricing, etc.). - Integrated contact form functionality using nodemailer for email handling. - Established global styles in globals.css and added animations with Framer Motion. - Included README.md for project documentation and setup instructions.
This commit is contained in:
169
app/api/contact/route.ts
Normal file
169
app/api/contact/route.ts
Normal file
@@ -0,0 +1,169 @@
|
||||
import { NextRequest, NextResponse } from 'next/server'
|
||||
import nodemailer from 'nodemailer'
|
||||
|
||||
export async function POST(request: NextRequest) {
|
||||
try {
|
||||
const body = await request.json()
|
||||
const { name, email, company, service, message } = body
|
||||
|
||||
// Validation des champs requis
|
||||
if (!name || !email || !service || !message) {
|
||||
return NextResponse.json(
|
||||
{ error: 'Tous les champs requis doivent être remplis.' },
|
||||
{ status: 400 }
|
||||
)
|
||||
}
|
||||
|
||||
// Validation de l'email
|
||||
const emailRegex = /^[^\s@]+@[^\s@]+\.[^\s@]+$/
|
||||
if (!emailRegex.test(email)) {
|
||||
return NextResponse.json(
|
||||
{ error: 'Adresse email invalide.' },
|
||||
{ status: 400 }
|
||||
)
|
||||
}
|
||||
|
||||
// Configuration du transporteur SMTP OVH
|
||||
const transporter = nodemailer.createTransport({
|
||||
host: process.env.SMTP_HOST || 'ssl0.ovh.net',
|
||||
port: parseInt(process.env.SMTP_PORT || '465'),
|
||||
secure: true, // true pour le port 465, false pour les autres ports
|
||||
auth: {
|
||||
user: process.env.SMTP_USER, // L'email d'envoi OVH
|
||||
pass: process.env.SMTP_PASSWORD, // Le mot de passe de l'email OVH
|
||||
},
|
||||
})
|
||||
|
||||
// Configuration de l'email
|
||||
const mailOptions = {
|
||||
from: process.env.SMTP_FROM || process.env.SMTP_USER,
|
||||
to: 'contact@runlock.re',
|
||||
replyTo: email,
|
||||
subject: `[Runlock.re] Nouvelle demande de contact - ${service}`,
|
||||
html: `
|
||||
<!DOCTYPE html>
|
||||
<html>
|
||||
<head>
|
||||
<meta charset="utf-8">
|
||||
<style>
|
||||
body {
|
||||
font-family: Arial, sans-serif;
|
||||
line-height: 1.6;
|
||||
color: #333;
|
||||
max-width: 600px;
|
||||
margin: 0 auto;
|
||||
padding: 20px;
|
||||
background-color: #f4f4f4;
|
||||
}
|
||||
.container {
|
||||
background-color: white;
|
||||
padding: 30px;
|
||||
border-radius: 10px;
|
||||
box-shadow: 0 2px 5px rgba(0,0,0,0.1);
|
||||
}
|
||||
h1 {
|
||||
color: #00ff88;
|
||||
border-bottom: 2px solid #00ff88;
|
||||
padding-bottom: 10px;
|
||||
}
|
||||
.info-row {
|
||||
margin: 15px 0;
|
||||
padding: 10px;
|
||||
background-color: #f9f9f9;
|
||||
border-left: 4px solid #00ff88;
|
||||
}
|
||||
.label {
|
||||
font-weight: bold;
|
||||
color: #666;
|
||||
display: inline-block;
|
||||
min-width: 120px;
|
||||
}
|
||||
.message-box {
|
||||
background-color: #f9f9f9;
|
||||
padding: 15px;
|
||||
border-radius: 5px;
|
||||
margin-top: 20px;
|
||||
white-space: pre-wrap;
|
||||
}
|
||||
.footer {
|
||||
margin-top: 30px;
|
||||
padding-top: 20px;
|
||||
border-top: 1px solid #ddd;
|
||||
font-size: 12px;
|
||||
color: #999;
|
||||
text-align: center;
|
||||
}
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
<div class="container">
|
||||
<h1>Nouvelle demande de contact</h1>
|
||||
|
||||
<div class="info-row">
|
||||
<span class="label">Nom :</span>
|
||||
<span>${name}</span>
|
||||
</div>
|
||||
|
||||
<div class="info-row">
|
||||
<span class="label">Email :</span>
|
||||
<span><a href="mailto:${email}">${email}</a></span>
|
||||
</div>
|
||||
|
||||
${company ? `
|
||||
<div class="info-row">
|
||||
<span class="label">Entreprise :</span>
|
||||
<span>${company}</span>
|
||||
</div>
|
||||
` : ''}
|
||||
|
||||
<div class="info-row">
|
||||
<span class="label">Service :</span>
|
||||
<span>${service}</span>
|
||||
</div>
|
||||
|
||||
<div class="message-box">
|
||||
<strong>Message :</strong><br><br>
|
||||
${message.replace(/\n/g, '<br>')}
|
||||
</div>
|
||||
|
||||
<div class="footer">
|
||||
<p>Cet email a été envoyé depuis le formulaire de contact de runlock.re</p>
|
||||
<p>Vous pouvez répondre directement à cet email pour contacter ${name}</p>
|
||||
</div>
|
||||
</div>
|
||||
</body>
|
||||
</html>
|
||||
`,
|
||||
text: `
|
||||
Nouvelle demande de contact - Runlock.re
|
||||
|
||||
Nom: ${name}
|
||||
Email: ${email}
|
||||
${company ? `Entreprise: ${company}\n` : ''}
|
||||
Service: ${service}
|
||||
|
||||
Message:
|
||||
${message}
|
||||
|
||||
---
|
||||
Cet email a été envoyé depuis le formulaire de contact de runlock.re
|
||||
Vous pouvez répondre directement à cet email pour contacter ${name}
|
||||
`,
|
||||
}
|
||||
|
||||
// Envoi de l'email
|
||||
await transporter.sendMail(mailOptions)
|
||||
|
||||
return NextResponse.json(
|
||||
{ message: 'Votre message a été envoyé avec succès !' },
|
||||
{ status: 200 }
|
||||
)
|
||||
} catch (error) {
|
||||
console.error('Erreur lors de l\'envoi de l\'email:', error)
|
||||
return NextResponse.json(
|
||||
{ error: 'Une erreur est survenue lors de l\'envoi de votre message. Veuillez réessayer.' },
|
||||
{ status: 500 }
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
114
app/globals.css
Normal file
114
app/globals.css
Normal file
@@ -0,0 +1,114 @@
|
||||
@tailwind base;
|
||||
@tailwind components;
|
||||
@tailwind utilities;
|
||||
|
||||
@layer base {
|
||||
html {
|
||||
scroll-behavior: smooth;
|
||||
}
|
||||
|
||||
body {
|
||||
@apply bg-security-darker text-gray-100 antialiased;
|
||||
}
|
||||
}
|
||||
|
||||
@layer components {
|
||||
.gradient-text {
|
||||
@apply bg-gradient-to-r from-security-accent via-green-400 to-emerald-400 bg-clip-text text-transparent;
|
||||
}
|
||||
|
||||
.gradient-bg {
|
||||
@apply bg-gradient-to-br from-security-darker via-security-dark to-security-darker;
|
||||
}
|
||||
|
||||
.security-glow {
|
||||
box-shadow: 0 0 30px rgba(0, 255, 136, 0.3), 0 0 60px rgba(0, 255, 136, 0.1);
|
||||
}
|
||||
|
||||
.card-hover {
|
||||
@apply transition-all duration-300 hover:scale-105 hover:shadow-2xl hover:shadow-security-accent/20;
|
||||
}
|
||||
|
||||
.section-padding {
|
||||
@apply py-24 px-4 sm:px-6 lg:px-8;
|
||||
}
|
||||
|
||||
.glass-effect {
|
||||
@apply bg-security-card/80 backdrop-blur-lg border border-security-border/50;
|
||||
}
|
||||
|
||||
.neon-border {
|
||||
@apply border-2 border-security-accent/40 shadow-lg shadow-security-accent/20;
|
||||
}
|
||||
|
||||
.card-dark {
|
||||
@apply bg-security-card border border-security-border rounded-2xl;
|
||||
}
|
||||
|
||||
/* Animations de fond */
|
||||
.animated-grid {
|
||||
background-image:
|
||||
linear-gradient(to right, rgba(26, 26, 36, 0.3) 1px, transparent 1px),
|
||||
linear-gradient(to bottom, rgba(26, 26, 36, 0.3) 1px, transparent 1px);
|
||||
background-size: 4rem 4rem;
|
||||
animation: gridMove 20s linear infinite;
|
||||
}
|
||||
|
||||
.floating-blob {
|
||||
animation: float 20s ease-in-out infinite;
|
||||
}
|
||||
|
||||
.pulse-glow {
|
||||
animation: pulseGlow 4s ease-in-out infinite;
|
||||
}
|
||||
|
||||
.gradient-shift {
|
||||
animation: gradientShift 8s ease infinite;
|
||||
background-size: 200% 200%;
|
||||
}
|
||||
}
|
||||
|
||||
@keyframes gridMove {
|
||||
0% {
|
||||
background-position: 0 0;
|
||||
}
|
||||
100% {
|
||||
background-position: 4rem 4rem;
|
||||
}
|
||||
}
|
||||
|
||||
@keyframes float {
|
||||
0%, 100% {
|
||||
transform: translateY(0px) translateX(0px);
|
||||
}
|
||||
33% {
|
||||
transform: translateY(-30px) translateX(20px);
|
||||
}
|
||||
66% {
|
||||
transform: translateY(20px) translateX(-20px);
|
||||
}
|
||||
}
|
||||
|
||||
@keyframes pulseGlow {
|
||||
0%, 100% {
|
||||
opacity: 0.5;
|
||||
transform: scale(1);
|
||||
}
|
||||
50% {
|
||||
opacity: 0.8;
|
||||
transform: scale(1.1);
|
||||
}
|
||||
}
|
||||
|
||||
@keyframes gradientShift {
|
||||
0% {
|
||||
background-position: 0% 50%;
|
||||
}
|
||||
50% {
|
||||
background-position: 100% 50%;
|
||||
}
|
||||
100% {
|
||||
background-position: 0% 50%;
|
||||
}
|
||||
}
|
||||
|
||||
32
app/layout.tsx
Normal file
32
app/layout.tsx
Normal file
@@ -0,0 +1,32 @@
|
||||
import type { Metadata } from 'next'
|
||||
import { Inter } from 'next/font/google'
|
||||
import './globals.css'
|
||||
import Header from '@/components/Header'
|
||||
import Footer from '@/components/Footer'
|
||||
|
||||
const inter = Inter({ subsets: ['latin'] })
|
||||
|
||||
export const metadata: Metadata = {
|
||||
title: 'Runlock.re - Expert Vaultwarden à la Réunion | Sécurisez vos mots de passe',
|
||||
description: 'Expert Vaultwarden à la Réunion - Hébergement sécurisé, installation NAS et Mini PC. Solutions de gestion de mots de passe pour entreprises 974.',
|
||||
keywords: 'Vaultwarden, Réunion, gestionnaire de mots de passe, sécurité, NAS, Bitwarden',
|
||||
}
|
||||
|
||||
export default function RootLayout({
|
||||
children,
|
||||
}: {
|
||||
children: React.ReactNode
|
||||
}) {
|
||||
return (
|
||||
<html lang="fr">
|
||||
<body className={inter.className}>
|
||||
<Header />
|
||||
<main className="min-h-screen">
|
||||
{children}
|
||||
</main>
|
||||
<Footer />
|
||||
</body>
|
||||
</html>
|
||||
)
|
||||
}
|
||||
|
||||
22
app/page.tsx
Normal file
22
app/page.tsx
Normal file
@@ -0,0 +1,22 @@
|
||||
import Hero from '@/components/sections/Hero'
|
||||
import Stats from '@/components/sections/Stats'
|
||||
import Services from '@/components/sections/Services'
|
||||
import Encryption from '@/components/sections/Encryption'
|
||||
import Pricing from '@/components/sections/Pricing'
|
||||
import FAQ from '@/components/sections/FAQ'
|
||||
import Contact from '@/components/sections/Contact'
|
||||
|
||||
export default function Home() {
|
||||
return (
|
||||
<>
|
||||
<Hero />
|
||||
<Stats />
|
||||
<Services />
|
||||
<Encryption />
|
||||
<Pricing />
|
||||
<FAQ />
|
||||
<Contact />
|
||||
</>
|
||||
)
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user