diff --git a/backend/package.json b/backend/package.json index e73b4af..303fd51 100644 --- a/backend/package.json +++ b/backend/package.json @@ -15,5 +15,8 @@ "dotenv": "^16.5.0", "express": "^5.1.0", "nodemailer": "^7.0.3" + }, "scripts": { + "start": "node server.js", + "test": "echo \"Error: no test specified\" && exit 1" } } diff --git a/backend/routes/email.js b/backend/routes/email.js index 5643d87..931ef56 100644 --- a/backend/routes/email.js +++ b/backend/routes/email.js @@ -5,48 +5,53 @@ const router = express.Router(); router.post('/send', async (req, res) => { const { name, email, message } = req.body; - if (!name ||!email ||!message) { - return res.status(400).json({ message: 'All fields are required.' }); + if (!name || !email || !message) { + return res.status(400).json({ errorKey: 'status_error_all_fields' }); } if (!/\S+@\S+\.\S+/.test(email)) { - return res.status(400).json({ message: 'Invalid email address.' }); + return res.status(400).json({ errorKey: 'status_error_invalid_email' }); } const transporter = nodemailer.createTransport({ host: process.env.SMTP_HOST, port: parseInt(process.env.SMTP_PORT || '587', 10), - secure: process.env.SMTP_SECURE === 'true', // true for 465, false for other ports + secure: process.env.SMTP_SECURE === 'true', auth: { - user: process.env.SMTP_USER, - pass: process.env.SMTP_PASS, - }, -// If using self-signed certificates or having issues with TLS: -// tls: { -// rejectUnauthorized: false // Use with caution, only for development/testing -// } -}); - -const mailOptions = { - from: `"${name}" <${process.env.SMTP_FROM_EMAIL || process.env.SMTP_USER}>`, // Use a configured FROM email or fallback - replyTo: email, - to: process.env.YOUR_RECEIVING_EMAIL, // Your email address to receive submissions - subject: `New Portfolio Contact: ${name}`, - text: `Name: ${name}\nEmail: ${email}\nMessage: ${message}`, - html: `

Name: ${name}

-

Email: ${email}

-

Message:

-

${message.replace(/\n/g, '
')}

`, -}; - + user: process.env.SMTP_USER, + pass: process.env.SMTP_PASS, + }, + }); + const mailOptions = { + from: `"${name}" <${process.env.SMTP_FROM_EMAIL || process.env.SMTP_USER}>`, + replyTo: email, + to: process.env.YOUR_RECEIVING_EMAIL, + subject: `New Portfolio Contact: ${name}`, + text: `Name: ${name}\nEmail: ${email}\nMessage: ${message}`, + html: `

Name: ${name}

+

Email: ${email}

+

Message:

+

${message.replace(/\n/g, '
')}

`, + }; try { await transporter.sendMail(mailOptions); res.status(200).json({ message: 'Message sent successfully!' }); } catch (error) { - console.error('Error sending email:', error); - // Provide a more generic error message to the client - res.status(500).json({ message: 'Failed to send message. Please try again later.' }); + if (error.code) { + switch (error.code) { + case 'EAUTH': + return res.status(500).json({ errorKey: 'smtp_auth_failed' }); + case 'ECONNECTION': + return res.status(500).json({ errorKey: 'smtp_connection_failed' }); + case 'EENVELOPE': + return res.status(400).json({ errorKey: 'smtp_invalid_recipient' }); + default: + return res.status(500).json({ errorKey: 'smtp_generic_error' }); + } + } + + res.status(500).json({ errorKey: 'server_unexpected_error' }); } }); diff --git a/frontend/README.md b/frontend/README.md index e215bc4..0d26152 100644 --- a/frontend/README.md +++ b/frontend/README.md @@ -1,36 +1,57 @@ -This is a [Next.js](https://nextjs.org) project bootstrapped with [`create-next-app`](https://nextjs.org/docs/app/api-reference/cli/create-next-app). +# Personal Portfolio & Blog - Frontend -## Getting Started +This is the frontend for my personal portfolio, built with [Next.js](https://nextjs.org), [Tailwind CSS](https://tailwindcss.com/), and [TypeScript](https://www.typescriptlang.org/). The project is designed to be a modern, responsive, and performant showcase of my skills and projects. -First, run the development server: +## ✨ Features +* **Internationalization (i18n)**: Supports both English and Portuguese, with language switching capabilities. +* **Dark Mode**: A sleek dark mode that can be toggled by the user, with preferences saved in local storage. +* **Component-Based Architecture**: Built with reusable React components for maintainability and scalability. +* **Responsive Design**: Fully responsive layout that looks great on all devices, from mobile phones to desktop screens. +* **Contact Form**: A functional contact form that communicates with a backend service to send emails. + +## 🛠️ Tech Stack + +* **Framework**: [Next.js 15](https://nextjs.org/) +* **Styling**: [Tailwind CSS 4](https://tailwindcss.com/) +* **Language**: [TypeScript](https://www.typescriptlang.org/) +* **Internationalization**: [next-intl](https://next-intl-docs.vercel.app/) +* **Icons**: [Heroicons](https://heroicons.com/) & [React Icons](https://react-icons.github.io/react-icons/) +* **Linting**: [ESLint](https://eslint.org/) + +## 🚀 Getting Started + +To get a local copy up and running, follow these simple steps. + +### Prerequisites + +* Node.js (v18.18 or later) +* npm, yarn, or pnpm + +### Installation & Development + +1. **Clone the repository:** + ```bash + git clone + cd /frontend + ``` + +2. **Install dependencies:** + ```bash + npm install + ``` + +3. **Run the development server:** + The development server uses Turbopack for faster performance. + ```bash + npm run dev + ``` + +4. **Open your browser:** + Navigate to [http://localhost:3000](http://localhost:3000) to see the result. + +### Building for Production + +To create a production-ready build, run: ```bash -npm run dev -# or -yarn dev -# or -pnpm dev -# or -bun dev -``` - -Open [http://localhost:3000](http://localhost:3000) with your browser to see the result. - -You can start editing the page by modifying `app/page.tsx`. The page auto-updates as you edit the file. - -This project uses [`next/font`](https://nextjs.org/docs/app/building-your-application/optimizing/fonts) to automatically optimize and load [Geist](https://vercel.com/font), a new font family for Vercel. - -## Learn More - -To learn more about Next.js, take a look at the following resources: - -- [Next.js Documentation](https://nextjs.org/docs) - learn about Next.js features and API. -- [Learn Next.js](https://nextjs.org/learn) - an interactive Next.js tutorial. - -You can check out [the Next.js GitHub repository](https://github.com/vercel/next.js) - your feedback and contributions are welcome! - -## Deploy on Vercel - -The easiest way to deploy your Next.js app is to use the [Vercel Platform](https://vercel.com/new?utm_medium=default-template&filter=next.js&utm_source=create-next-app&utm_campaign=create-next-app-readme) from the creators of Next.js. - -Check out our [Next.js deployment documentation](https://nextjs.org/docs/app/building-your-application/deploying) for more details. +npm run build \ No newline at end of file diff --git a/frontend/package-lock.json b/frontend/package-lock.json index cbbae25..39fb734 100644 --- a/frontend/package-lock.json +++ b/frontend/package-lock.json @@ -13,6 +13,7 @@ "next-intl": "^4.1.0", "react": "^19.0.0", "react-dom": "^19.0.0", + "react-hot-toast": "^2.5.2", "react-icons": "^5.5.0" }, "devDependencies": { @@ -2443,7 +2444,6 @@ "version": "3.1.3", "resolved": "https://registry.npmjs.org/csstype/-/csstype-3.1.3.tgz", "integrity": "sha512-M1uQkMl8rQK/szD0LNhtqxIPLpimGm8sOBwU7lLnCpSbTyY3yeU1Vc7l4KT5zT4s/yOxHH5O7tIuuLOCnLADRw==", - "dev": true, "license": "MIT" }, "node_modules/damerau-levenshtein": { @@ -3544,6 +3544,15 @@ "url": "https://github.com/sponsors/ljharb" } }, + "node_modules/goober": { + "version": "2.1.16", + "resolved": "https://registry.npmjs.org/goober/-/goober-2.1.16.tgz", + "integrity": "sha512-erjk19y1U33+XAMe1VTvIONHYoSqE4iS7BYUZfHaqeohLmnC0FdxEh7rQU+6MZ4OajItzjZFSRtVANrQwNq6/g==", + "license": "MIT", + "peerDependencies": { + "csstype": "^3.0.10" + } + }, "node_modules/gopd": { "version": "1.2.0", "resolved": "https://registry.npmjs.org/gopd/-/gopd-1.2.0.tgz", @@ -5203,6 +5212,23 @@ "react": "^19.1.0" } }, + "node_modules/react-hot-toast": { + "version": "2.5.2", + "resolved": "https://registry.npmjs.org/react-hot-toast/-/react-hot-toast-2.5.2.tgz", + "integrity": "sha512-Tun3BbCxzmXXM7C+NI4qiv6lT0uwGh4oAfeJyNOjYUejTsm35mK9iCaYLGv8cBz9L5YxZLx/2ii7zsIwPtPUdw==", + "license": "MIT", + "dependencies": { + "csstype": "^3.1.3", + "goober": "^2.1.16" + }, + "engines": { + "node": ">=10" + }, + "peerDependencies": { + "react": ">=16", + "react-dom": ">=16" + } + }, "node_modules/react-icons": { "version": "5.5.0", "resolved": "https://registry.npmjs.org/react-icons/-/react-icons-5.5.0.tgz", diff --git a/frontend/package.json b/frontend/package.json index 9210246..0f4dcef 100644 --- a/frontend/package.json +++ b/frontend/package.json @@ -14,6 +14,7 @@ "next-intl": "^4.1.0", "react": "^19.0.0", "react-dom": "^19.0.0", + "react-hot-toast": "^2.5.2", "react-icons": "^5.5.0" }, "devDependencies": { diff --git a/frontend/public/logo.png b/frontend/public/logo.png index 2f9e370..6f69895 100644 Binary files a/frontend/public/logo.png and b/frontend/public/logo.png differ diff --git a/frontend/src/app/(i18n)/[locale]/layout.tsx b/frontend/src/app/(i18n)/[locale]/layout.tsx index ee1a4df..0751921 100644 --- a/frontend/src/app/(i18n)/[locale]/layout.tsx +++ b/frontend/src/app/(i18n)/[locale]/layout.tsx @@ -2,6 +2,7 @@ import { Inter } from "next/font/google"; import Header from "@/app/components/Header"; // Usando o Header que forneci import Footer from "@/app/components/Footer"; import "@/app/globals.css"; // Importando o CSS global +import { Toaster } from 'react-hot-toast'; // Importações importantes do next-intl import {hasLocale, Locale, NextIntlClientProvider} from 'next-intl'; @@ -45,11 +46,37 @@ export default async function RootLayout({children, params}: Props) { +
{children}