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:
SarTron-NorthBlue
2025-11-14 16:15:21 +04:00
commit bed824059a
24 changed files with 4229 additions and 0 deletions

169
app/api/contact/route.ts Normal file
View 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
View 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
View 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
View 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 />
</>
)
}