Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
23 changes: 22 additions & 1 deletion .claude/settings.local.json
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,28 @@
"Bash(git add:*)",
"Bash(GIT_TRACE=1 git commit:*)",
"Bash(cmd /c \"del /f \"\"d:\\\\Usuario\\\\Desktop\\\\trdtyyyy\\\\Portfolio\\\\app\\\\projects\\\\[id]\\\\page.tsx\"\" && del /f \"\"d:\\\\Usuario\\\\Desktop\\\\trdtyyyy\\\\Portfolio\\\\app\\\\projects\\\\[id]\\\\not-found.tsx\"\"\")",
"Bash(cmd /c \"del /f \"\"d:\\\\Usuario\\\\Desktop\\\\trdtyyyy\\\\Portfolio\\\\app\\\\projects\\\\[id]\\\\page.tsx\"\"\")"
"Bash(cmd /c \"del /f \"\"d:\\\\Usuario\\\\Desktop\\\\trdtyyyy\\\\Portfolio\\\\app\\\\projects\\\\[id]\\\\page.tsx\"\"\")",
"Bash(pnpm build:*)",
"Bash(gh pr create --title 'Second-pass redesign: B&W palette, typography polish, image carousel, bug fixes' --body ':*)",
"Bash(pip install:*)",
"Bash(pip3 install:*)",
"Bash(where pip:*)",
"Bash(winget install:*)",
"Bash(export PATH=\"$PATH:/c/Program Files/GitHub CLI\")",
"Bash('/c/Program Files/GitHub CLI/gh.exe' pr create --title 'Second-pass redesign: B&W palette, typography polish, image carousel, bug fixes' --body ':*)",
"Bash(\"/c/Program Files/GitHub CLI/gh.exe\" auth login --web)",
"Bash(\"/c/Program Files/GitHub CLI/gh.exe\" pr checks 6 --repo Repetto-A/Portfolio)",
"Bash(\"/c/Program Files/GitHub CLI/gh.exe\" run view 24492383559 --repo Repetto-A/Portfolio --log-failed)",
"Bash(pnpm install:*)",
"Bash(gh pr:*)",
"Bash(gh.exe pr:*)",
"Bash(where gh.exe)",
"Bash(cmd /c \"gh pr checks\")",
"Bash(cmd /c \"where gh\")",
"Bash(cmd /c \"where /R C:\\\\Users\\\\conta gh.exe\")",
"Bash(\"/c/Program Files/GitHub CLI/gh.exe\" pr checks)",
"Bash(\"/c/Program Files/GitHub CLI/gh.exe\" run view 24496201059 --log-failed)",
"Bash(pnpm format:*)"
]
}
}
6 changes: 3 additions & 3 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,7 @@ jobs:
- name: Setup pnpm
uses: pnpm/action-setup@v3
with:
version: 8
version: 10

- name: Get pnpm store directory
id: pnpm-cache
Expand Down Expand Up @@ -62,7 +62,7 @@ jobs:
- name: Setup pnpm
uses: pnpm/action-setup@v3
with:
version: 8
version: 10

- name: Get pnpm store directory
id: pnpm-cache
Expand Down Expand Up @@ -99,7 +99,7 @@ jobs:
- name: Setup pnpm
uses: pnpm/action-setup@v3
with:
version: 8
version: 10

- name: Get pnpm store directory
id: pnpm-cache
Expand Down
3 changes: 3 additions & 0 deletions .prettierignore
Original file line number Diff line number Diff line change
Expand Up @@ -10,3 +10,6 @@ yarn.lock
.gitignore
public
*.md
.claude
test-results
e2e
62 changes: 31 additions & 31 deletions app/api/contact/route.ts
Original file line number Diff line number Diff line change
@@ -1,39 +1,39 @@
import { NextResponse } from "next/server";
import nodemailer from "nodemailer";
import { NextResponse } from "next/server"
import nodemailer from "nodemailer"

export async function POST(request: Request) {
try {
const body = await request.json();
const body = await request.json()

// Validaciones básicas
if (!body.name || !body.email || !body.subject || !body.message) {
return NextResponse.json(
{ success: false, error: "Todos los campos son obligatorios" },
{ status: 400 }
);
)
}

// Validar formato de email
const emailRegex = /^[^\s@]+@[^\s@]+\.[^\s@]+$/;
const emailRegex = /^[^\s@]+@[^\s@]+\.[^\s@]+$/
if (!emailRegex.test(body.email)) {
return NextResponse.json(
{ success: false, error: "Formato de correo electrónico inválido" },
{ status: 400 }
);
)
}

// Verificar variables de entorno
const requiredEnvVars = ['SMTP_HOST', 'SMTP_PORT', 'SMTP_USER', 'SMTP_PASSWORD', 'SMTP_FROM'];
const missingVars = requiredEnvVars.filter(varName => !process.env[varName]);
const requiredEnvVars = ["SMTP_HOST", "SMTP_PORT", "SMTP_USER", "SMTP_PASSWORD", "SMTP_FROM"]
const missingVars = requiredEnvVars.filter((varName) => !process.env[varName])

if (missingVars.length > 0) {
return NextResponse.json(
{
success: false,
error: `Error de configuración del servidor. Por favor, contacta al administrador.`
{
success: false,
error: `Error de configuración del servidor. Por favor, contacta al administrador.`,
},
{ status: 500 }
);
)
}

// Configuración del transporte SMTP
Expand All @@ -46,9 +46,9 @@ export async function POST(request: Request) {
pass: process.env.SMTP_PASSWORD,
},
tls: {
rejectUnauthorized: false
}
});
rejectUnauthorized: false,
},
})

// Opciones del correo
const mailOptions = {
Expand Down Expand Up @@ -78,26 +78,26 @@ export async function POST(request: Request) {
</div>
</div>
`,
};
}

const info = await transporter.sendMail(mailOptions);
await transporter.sendMail(mailOptions)

return NextResponse.json(
{
{
success: true,
message: "¡Mensaje enviado con éxito! Me pondré en contacto contigo pronto.",
},
{ status: 200 }
);
} catch (error: any) {
console.error('Error sending email:', error);
)
} catch (error: unknown) {
console.error("Error sending email:", error)
return NextResponse.json(
{
success: false,
error: "Error al procesar la solicitud. Por favor, inténtalo de nuevo más tarde."
{
success: false,
error: "Error al procesar la solicitud. Por favor, inténtalo de nuevo más tarde.",
},
{ status: 500 }
);
)
}
}

Expand All @@ -106,9 +106,9 @@ export async function OPTIONS() {
return new Response(null, {
status: 200,
headers: {
'Access-Control-Allow-Origin': '*',
'Access-Control-Allow-Methods': 'POST, OPTIONS',
'Access-Control-Allow-Headers': 'Content-Type',
"Access-Control-Allow-Origin": "*",
"Access-Control-Allow-Methods": "POST, OPTIONS",
"Access-Control-Allow-Headers": "Content-Type",
},
});
})
}
2 changes: 1 addition & 1 deletion app/api/og/route.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -99,7 +99,7 @@ export async function GET(req: NextRequest) {
{
width: 1200,
height: 630,
},
}
)
} catch (error) {
console.error("Error generating OG image:", error)
Expand Down
106 changes: 94 additions & 12 deletions app/globals.css
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@
--card-foreground: oklch(0.145 0 0);
--popover: oklch(1 0 0);
--popover-foreground: oklch(0.145 0 0);
--primary: oklch(0.205 0 0);
--primary: oklch(0.145 0 0);
--primary-foreground: oklch(0.985 0 0);
--secondary: oklch(0.97 0 0);
--secondary-foreground: oklch(0.205 0 0);
Expand All @@ -22,7 +22,7 @@
--destructive-foreground: oklch(0.577 0.245 27.325);
--border: oklch(0.922 0 0);
--input: oklch(0.922 0 0);
--ring: oklch(0.708 0 0);
--ring: oklch(0.145 0 0);
--chart-1: oklch(0.646 0.222 41.116);
--chart-2: oklch(0.6 0.118 184.704);
--chart-3: oklch(0.398 0.07 227.392);
Expand All @@ -42,12 +42,12 @@
.dark {
--background: oklch(0.145 0 0);
--foreground: oklch(0.985 0 0);
--card: oklch(0.145 0 0);
--card: oklch(0.16 0 0);
--card-foreground: oklch(0.985 0 0);
--popover: oklch(0.145 0 0);
--popover: oklch(0.16 0 0);
--popover-foreground: oklch(0.985 0 0);
--primary: oklch(0.985 0 0);
--primary-foreground: oklch(0.205 0 0);
--primary: oklch(0.922 0 0);
--primary-foreground: oklch(0.145 0 0);
--secondary: oklch(0.269 0 0);
--secondary-foreground: oklch(0.985 0 0);
--muted: oklch(0.269 0 0);
Expand All @@ -58,7 +58,7 @@
--destructive-foreground: oklch(0.637 0.237 25.331);
--border: oklch(0.269 0 0);
--input: oklch(0.269 0 0);
--ring: oklch(0.439 0 0);
--ring: oklch(0.922 0 0);
--chart-1: oklch(0.488 0.243 264.376);
--chart-2: oklch(0.696 0.17 162.48);
--chart-3: oklch(0.769 0.188 70.08);
Expand Down Expand Up @@ -111,6 +111,8 @@
--color-sidebar-accent-foreground: var(--sidebar-accent-foreground);
--color-sidebar-border: var(--sidebar-border);
--color-sidebar-ring: var(--sidebar-ring);
--font-sans: var(--font-inter), ui-sans-serif, system-ui, sans-serif;
--font-mono: var(--font-jetbrains-mono), ui-monospace, monospace;
}

html {
Expand All @@ -136,16 +138,16 @@ body {
}

::-webkit-scrollbar-track {
background: rgb(var(--background));
background: var(--background);
}

::-webkit-scrollbar-thumb {
background: rgb(var(--muted));
background: var(--muted);
border-radius: 4px;
}

::-webkit-scrollbar-thumb:hover {
background: rgb(var(--muted-foreground));
background: var(--muted-foreground);
}

@layer base {
Expand All @@ -155,6 +157,8 @@ body {

body {
@apply bg-background text-foreground;
letter-spacing: -0.011em;
line-height: 1.6;
}

/* Ensure tap targets are at least 44x44px on touch devices */
Expand Down Expand Up @@ -199,8 +203,8 @@ body {
left: -9999px;
z-index: 999;
padding: 1rem 1.5rem;
background-color: rgb(var(--background));
color: rgb(var(--foreground));
background-color: var(--background);
color: var(--foreground);
text-decoration: none;
border-radius: 0.5rem;
}
Expand All @@ -222,6 +226,84 @@ body {
}
}

/* ── Custom animations ─────────────────────────────── */
@keyframes fade-in-up {
from {
opacity: 0;
transform: translateY(18px);
}
to {
opacity: 1;
transform: translateY(0);
}
}

@keyframes fade-in {
from { opacity: 0; }
to { opacity: 1; }
}

/* Card entrance on scroll — progressive enhancement */
@supports (animation-timeline: view()) {
@media (prefers-reduced-motion: no-preference) {
.scroll-reveal {
animation: fade-in-up 0.55s cubic-bezier(0.16, 1, 0.3, 1) both;
animation-timeline: view();
animation-range: entry 0% entry 25%;
}
}
}

/* Staggered scroll-reveal variants for grid cards */
@supports (animation-timeline: view()) {
@media (prefers-reduced-motion: no-preference) {
.scroll-reveal-stagger-1 {
animation: fade-in-up 0.55s cubic-bezier(0.16, 1, 0.3, 1) both;
animation-timeline: view();
animation-range: entry 0% entry 25%;
}
.scroll-reveal-stagger-2 {
animation: fade-in-up 0.55s cubic-bezier(0.16, 1, 0.3, 1) both;
animation-timeline: view();
animation-range: entry 3% entry 28%;
}
.scroll-reveal-stagger-3 {
animation: fade-in-up 0.55s cubic-bezier(0.16, 1, 0.3, 1) both;
animation-timeline: view();
animation-range: entry 6% entry 31%;
}
.scroll-reveal-stagger-4 {
animation: fade-in-up 0.55s cubic-bezier(0.16, 1, 0.3, 1) both;
animation-timeline: view();
animation-range: entry 9% entry 34%;
}
}
}

/* Utility: gradient text (primary → violet) */
.gradient-text {
background: linear-gradient(
135deg,
oklch(0.52 0.22 263) 0%,
oklch(0.55 0.25 295) 100%
);
-webkit-background-clip: text;
-webkit-text-fill-color: transparent;
background-clip: text;
}

.dark .gradient-text {
background: linear-gradient(
135deg,
oklch(0.68 0.18 263) 0%,
oklch(0.72 0.22 295) 100%
);
-webkit-background-clip: text;
-webkit-text-fill-color: transparent;
background-clip: text;
}

/* ── Print styles ──────────────────────────────────── */
/* Print styles */
@media print {
nav,
Expand Down
Loading
Loading