Skip to content

Commit 19b8e7f

Browse files
committed
'xd'
1 parent 19146db commit 19b8e7f

File tree

15 files changed

+646
-9
lines changed

15 files changed

+646
-9
lines changed

.env

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
EMAIL_HOST=smtp.gmail.com
2+
EMAIL_PORT=587
3+
EMAIL_SECURE=false
4+
EMAIL_USER=gregoriowhite333@gmail.com
5+
EMAIL_PASS='ydnm jbqn vdxz kdyv'
6+
EMAIL_FROM=gregoriowhite333@gmail.com
7+
EMAIL_TO=gregoriowhite333@gmail.com

app/Main.tsx

Lines changed: 10 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,18 +1,23 @@
1-
import Link from '@/components/Link'
2-
import Tag from '@/components/Tag'
3-
import siteMetadata from '@/data/siteMetadata'
4-
import { formatDate } from 'pliny/utils/formatDate'
5-
import NewsletterForm from 'pliny/ui/NewsletterForm'
61
import HomeMain from '@/components/home/Home'
72
import SkillsSlider from '@/components/home/Skills'
3+
import Project from '@/components/home/Projects'
4+
import StudyTimeline from '@/components/home/StudyTimeLine'
5+
import AboutMe from '@/components/home/Aboutme'
6+
import Contact from '@/components/home/Contact'
7+
import StickyMenu from '@/components/home/MenuSticky'
88

99
const MAX_DISPLAY = 2
1010

1111
export default function Home({ posts }) {
1212
return (
1313
<>
14+
<StickyMenu />
1415
<HomeMain />
1516
<SkillsSlider />
17+
<StudyTimeline />
18+
<Project />
19+
<AboutMe />
20+
<Contact />
1621
</>
1722
)
1823
}

app/api/contact/route.ts

Lines changed: 42 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,42 @@
1+
import { NextResponse } from 'next/server'
2+
import nodemailer from 'nodemailer'
3+
4+
export async function POST(request: Request) {
5+
const body = await request.json()
6+
7+
// Configurar el transporter de nodemailer
8+
const transporter = nodemailer.createTransport({
9+
host: process.env.EMAIL_HOST,
10+
port: parseInt(process.env.EMAIL_PORT || '587'),
11+
secure: process.env.EMAIL_SECURE === 'true',
12+
auth: {
13+
user: process.env.EMAIL_USER,
14+
pass: process.env.EMAIL_PASS,
15+
},
16+
})
17+
18+
try {
19+
// Enviar el email
20+
await transporter.sendMail({
21+
from: process.env.EMAIL_FROM,
22+
to: process.env.EMAIL_TO,
23+
subject: 'Nuevo mensaje de contacto',
24+
text: `
25+
Nombre: ${body.name}
26+
Email: ${body.email}
27+
Mensaje: ${body.message}
28+
`,
29+
html: `
30+
<h1>Nuevo mensaje de contacto</h1>
31+
<p><strong>Nombre:</strong> ${body.name}</p>
32+
<p><strong>Email:</strong> ${body.email}</p>
33+
<p><strong>Mensaje:</strong> ${body.message}</p>
34+
`,
35+
})
36+
37+
return NextResponse.json({ message: 'Mensaje enviado con éxito' })
38+
} catch (error) {
39+
console.error('Error al enviar el email:', error)
40+
return NextResponse.json({ message: 'Error al enviar el mensaje' }, { status: 500 })
41+
}
42+
}

components/home/Aboutme.tsx

Lines changed: 73 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,73 @@
1+
import Image from 'next/image'
2+
3+
export default function AboutMe() {
4+
return (
5+
<section className="bg-white px-4 py-12 sm:px-6 lg:px-8" id="about">
6+
<div className="mx-auto max-w-7xl">
7+
<div className="lg:grid lg:grid-cols-2 lg:items-center lg:gap-8">
8+
<div className="relative">
9+
<h2 className="text-3xl font-extrabold text-gray-900 sm:text-4xl">Sobre mí</h2>
10+
<p className="mt-3 text-lg text-gray-500">
11+
Soy un desarrollador web apasionado con experiencia en crear aplicaciones web modernas
12+
y responsivas. Mi objetivo es combinar diseño atractivo con funcionalidad robusta para
13+
ofrecer experiencias de usuario excepcionales.
14+
</p>
15+
<dl className="mt-10 space-y-10">
16+
{[
17+
{
18+
name: 'Desarrollo Frontend',
19+
description:
20+
'Experto en React, Next.js y Tailwind CSS para crear interfaces de usuario dinámicas y atractivas.',
21+
},
22+
{
23+
name: 'Desarrollo Backend',
24+
description:
25+
'Experiencia en Node.js y Express para construir APIs robustas y escalables.',
26+
},
27+
{
28+
name: 'Bases de Datos',
29+
description: 'Conocimientos en SQL y NoSQL, incluyendo PostgreSQL y MongoDB.',
30+
},
31+
].map((skill) => (
32+
<div key={skill.name} className="relative">
33+
<dt>
34+
<div className="absolute flex h-12 w-12 items-center justify-center rounded-md bg-indigo-500 text-white">
35+
<svg
36+
className="h-6 w-6"
37+
xmlns="http://www.w3.org/2000/svg"
38+
fill="none"
39+
viewBox="0 0 24 24"
40+
stroke="currentColor"
41+
aria-hidden="true"
42+
>
43+
<path
44+
strokeLinecap="round"
45+
strokeLinejoin="round"
46+
strokeWidth={2}
47+
d="M13 10V3L4 14h7v7l9-11h-7z"
48+
/>
49+
</svg>
50+
</div>
51+
<p className="ml-16 text-lg font-medium leading-6 text-gray-900">
52+
{skill.name}
53+
</p>
54+
</dt>
55+
<dd className="ml-16 mt-2 text-base text-gray-500">{skill.description}</dd>
56+
</div>
57+
))}
58+
</dl>
59+
</div>
60+
<div className="relative -mx-4 mt-10 lg:mt-0" aria-hidden="true">
61+
<Image
62+
className="relative mx-auto rounded-lg shadow-lg"
63+
src="/placeholder.svg?height=500&width=500"
64+
alt="Foto de perfil"
65+
width={500}
66+
height={500}
67+
/>
68+
</div>
69+
</div>
70+
</div>
71+
</section>
72+
)
73+
}

components/home/Contact.tsx

Lines changed: 87 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,87 @@
1+
import ContactForm from './ContactForm'
2+
3+
export default function Contact() {
4+
return (
5+
<section className="bg-white px-4 py-12 sm:px-6 lg:px-8">
6+
<div className="mx-auto max-w-7xl">
7+
<div className="lg:grid lg:grid-cols-2 lg:gap-8">
8+
<div>
9+
<h2 className="text-3xl font-extrabold text-gray-900 sm:text-4xl">Contacto</h2>
10+
<p className="mt-3 text-lg text-gray-500">
11+
¿Tienes alguna pregunta o propuesta? No dudes en contactarme. Estaré encantado de
12+
escucharte y responder lo antes posible.
13+
</p>
14+
<dl className="mt-8 text-base text-gray-500">
15+
<div className="mt-6">
16+
<dt className="sr-only">Dirección de correo electrónico</dt>
17+
<dd className="flex">
18+
<svg
19+
className="h-6 w-6 flex-shrink-0 text-gray-400"
20+
fill="none"
21+
viewBox="0 0 24 24"
22+
stroke="currentColor"
23+
>
24+
<path
25+
strokeLinecap="round"
26+
strokeLinejoin="round"
27+
strokeWidth={2}
28+
d="M3 8l7.89 5.26a2 2 0 002.22 0L21 8M5 19h14a2 2 0 002-2V7a2 2 0 00-2-2H5a2 2 0 00-2 2v10a2 2 0 002 2z"
29+
/>
30+
</svg>
31+
<span className="ml-3">gregoriowhite333@gmail.com</span>
32+
</dd>
33+
</div>
34+
<div className="mt-3">
35+
<dt className="sr-only">Número de teléfono</dt>
36+
<dd className="flex">
37+
<svg
38+
className="h-6 w-6 flex-shrink-0 text-gray-400"
39+
fill="none"
40+
viewBox="0 0 24 24"
41+
stroke="currentColor"
42+
>
43+
<path
44+
strokeLinecap="round"
45+
strokeLinejoin="round"
46+
strokeWidth={2}
47+
d="M3 5a2 2 0 012-2h3.28a1 1 0 01.948.684l1.498 4.493a1 1 0 01-.502 1.21l-2.257 1.13a11.042 11.042 0 005.516 5.516l1.13-2.257a1 1 0 011.21-.502l4.493 1.498a1 1 0 01.684.949V19a2 2 0 01-2 2h-1C9.716 21 3 14.284 3 6V5z"
48+
/>
49+
</svg>
50+
<span className="ml-3">+34 695 866 771</span>
51+
</dd>
52+
</div>
53+
<div className="mt-3">
54+
<dt className="sr-only">Ubicación</dt>
55+
<dd className="flex">
56+
<svg
57+
className="h-6 w-6 flex-shrink-0 text-gray-400"
58+
fill="none"
59+
viewBox="0 0 24 24"
60+
stroke="currentColor"
61+
>
62+
<path
63+
strokeLinecap="round"
64+
strokeLinejoin="round"
65+
strokeWidth={2}
66+
d="M17.657 16.657L13.414 20.9a1.998 1.998 0 01-2.827 0l-4.244-4.243a8 8 0 1111.314 0z"
67+
/>
68+
<path
69+
strokeLinecap="round"
70+
strokeLinejoin="round"
71+
strokeWidth={2}
72+
d="M15 11a3 3 0 11-6 0 3 3 0 016 0z"
73+
/>
74+
</svg>
75+
<span className="ml-3">Barcelona, España</span>
76+
</dd>
77+
</div>
78+
</dl>
79+
</div>
80+
<div className="mt-8 lg:mt-0">
81+
<ContactForm />
82+
</div>
83+
</div>
84+
</div>
85+
</section>
86+
)
87+
}

components/home/ContactForm.tsx

Lines changed: 98 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,98 @@
1+
'use client'
2+
3+
import { useState } from 'react'
4+
import { useForm } from 'react-hook-form'
5+
import { zodResolver } from '@hookform/resolvers/zod'
6+
import * as z from 'zod'
7+
8+
const formSchema = z.object({
9+
name: z.string().min(2, { message: 'El nombre debe tener al menos 2 caracteres.' }),
10+
email: z.string().email({ message: 'Por favor, introduce un email válido.' }),
11+
message: z.string().min(10, { message: 'El mensaje debe tener al menos 10 caracteres.' }),
12+
})
13+
14+
export default function ContactForm() {
15+
const {
16+
register,
17+
handleSubmit,
18+
formState: { errors },
19+
reset,
20+
} = useForm({
21+
resolver: zodResolver(formSchema),
22+
})
23+
const [isSubmitting, setIsSubmitting] = useState(false)
24+
const [submitStatus, setSubmitStatus] = useState<'idle' | 'success' | 'error'>('idle')
25+
26+
async function onSubmit(data: z.infer<typeof formSchema>) {
27+
setIsSubmitting(true)
28+
setSubmitStatus('idle')
29+
try {
30+
const response = await fetch('/api/contact', {
31+
method: 'POST',
32+
headers: {
33+
'Content-Type': 'application/json',
34+
},
35+
body: JSON.stringify(data),
36+
})
37+
38+
if (response.ok) {
39+
setSubmitStatus('success')
40+
reset()
41+
} else {
42+
throw new Error('Error al enviar el mensaje')
43+
}
44+
} catch (error) {
45+
setSubmitStatus('error')
46+
} finally {
47+
setIsSubmitting(false)
48+
}
49+
}
50+
51+
return (
52+
<form onSubmit={handleSubmit(onSubmit)} className="space-y-6" id="contact">
53+
<div>
54+
<input
55+
{...register('name')}
56+
placeholder="Tu nombre"
57+
className={`w-full rounded-md border px-3 py-2 ${errors.name ? 'border-red-500' : 'border-gray-300'} focus:outline-none focus:ring-2 focus:ring-blue-500`}
58+
/>
59+
{errors.name && <p className="mt-1 text-sm text-red-500">{errors.name.message}</p>}
60+
</div>
61+
<div>
62+
<input
63+
{...register('email')}
64+
type="email"
65+
placeholder="Tu email"
66+
className={`w-full rounded-md border px-3 py-2 ${errors.email ? 'border-red-500' : 'border-gray-300'} focus:outline-none focus:ring-2 focus:ring-blue-500`}
67+
/>
68+
{errors.email && <p className="mt-1 text-sm text-red-500">{errors.email.message}</p>}
69+
</div>
70+
<div>
71+
<textarea
72+
{...register('message')}
73+
placeholder="Tu mensaje"
74+
rows={4}
75+
className={`w-full rounded-md border px-3 py-2 ${errors.message ? 'border-red-500' : 'border-gray-300'} focus:outline-none focus:ring-2 focus:ring-blue-500`}
76+
/>
77+
{errors.message && <p className="mt-1 text-sm text-red-500">{errors.message.message}</p>}
78+
</div>
79+
<button
80+
type="submit"
81+
disabled={isSubmitting}
82+
className="w-full rounded-md bg-blue-600 px-4 py-2 text-white hover:bg-blue-700 focus:outline-none focus:ring-2 focus:ring-blue-500 focus:ring-offset-2 disabled:opacity-50"
83+
>
84+
{isSubmitting ? 'Enviando...' : 'Enviar mensaje'}
85+
</button>
86+
{submitStatus === 'success' && (
87+
<p className="mt-3 text-sm text-green-600">
88+
Mensaje enviado con éxito. Gracias por contactarnos.
89+
</p>
90+
)}
91+
{submitStatus === 'error' && (
92+
<p className="mt-3 text-sm text-red-600">
93+
Hubo un problema al enviar tu mensaje. Por favor, inténtalo de nuevo.
94+
</p>
95+
)}
96+
</form>
97+
)
98+
}

components/home/Home.tsx

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@ import { Github, Linkedin, Twitter } from 'lucide-react'
44

55
export default function HomeMain() {
66
return (
7-
<div className="z-10 w-full max-w-5xl items-center justify-between font-mono text-sm">
7+
<div className="z-10 w-full max-w-5xl items-center justify-between font-mono text-sm" id="home">
88
<div className="flex flex-col items-center space-y-8">
99
<Image
1010
src="/static/images/logoluffy.jpeg"

0 commit comments

Comments
 (0)