diff --git a/package-lock.json b/package-lock.json
index 73cec81..8c0aae3 100644
--- a/package-lock.json
+++ b/package-lock.json
@@ -11,7 +11,8 @@
"@astrojs/node": "^11.0.2",
"@astrojs/sitemap": "^3.7.3",
"astro": "^7.1.4",
- "dotenv": "^17.4.2"
+ "dotenv": "^17.4.2",
+ "resend": "^6.22.0"
},
"engines": {
"node": ">=22.12.0"
@@ -1903,6 +1904,12 @@
"integrity": "sha512-83yeghZ2xxin3Nj8z1NMd/NCuca+gsYXswywDy5bHvwlWL8tpTQmzGeUuHd9FC3E/SBEMvzJRwWEOz5gGes9Qg==",
"license": "MIT"
},
+ "node_modules/@stablelib/base64": {
+ "version": "1.0.1",
+ "resolved": "https://registry.npmjs.org/@stablelib/base64/-/base64-1.0.1.tgz",
+ "integrity": "sha512-1bnPQqSxSuc3Ii6MhBysoWCg58j97aUjuCSZrGSmDxNqtytIi0k8utUenAwTZN4V5mXXYGsVUI9zeBqy+jBOSQ==",
+ "license": "MIT"
+ },
"node_modules/@tybys/wasm-util": {
"version": "0.10.3",
"resolved": "https://registry.npmjs.org/@tybys/wasm-util/-/wasm-util-0.10.3.tgz",
@@ -2630,6 +2637,12 @@
"integrity": "sha512-fjquC59cD7CyW6urNXK0FBufkZcoiGG80wTuPujX590cB5Ttln20E2UB4S/WARVqhXffZl2LNgS+gQdPIIim/g==",
"license": "MIT"
},
+ "node_modules/fast-sha256": {
+ "version": "1.3.0",
+ "resolved": "https://registry.npmjs.org/fast-sha256/-/fast-sha256-1.3.0.tgz",
+ "integrity": "sha512-n11RGP/lrWEFI/bWdygLxhI+pVeo1ZYIVwvvPkW7azl/rOy+F3HYRZ2K5zeE9mmkhQppyv9sQFx0JM9UabnpPQ==",
+ "license": "Unlicense"
+ },
"node_modules/fast-string-truncated-width": {
"version": "3.0.3",
"resolved": "https://registry.npmjs.org/fast-string-truncated-width/-/fast-string-truncated-width-3.0.3.tgz",
@@ -3629,6 +3642,12 @@
"url": "https://github.com/sponsors/jonschlinkert"
}
},
+ "node_modules/postal-mime": {
+ "version": "2.7.5",
+ "resolved": "https://registry.npmjs.org/postal-mime/-/postal-mime-2.7.5.tgz",
+ "integrity": "sha512-GNEXKvWFQnbgO5NlrGzVa0FmWzBZ24PersAWErttSg1Hjpf0ATxTwS5DOMGaOpTG6bUh5cTr7xi0jAD942wCJA==",
+ "license": "MIT-0"
+ },
"node_modules/postcss": {
"version": "8.5.23",
"resolved": "https://registry.npmjs.org/postcss/-/postcss-8.5.23.tgz",
@@ -3741,6 +3760,27 @@
"integrity": "sha512-8VhliFJAWRaUiVvREIiW2NXXTmHs4vMNnSzuJVhscgmGav3g9VDxLrQndI3dZZVVdp0ZO/5v0xmX516/7M9cng==",
"license": "MIT"
},
+ "node_modules/resend": {
+ "version": "6.22.0",
+ "resolved": "https://registry.npmjs.org/resend/-/resend-6.22.0.tgz",
+ "integrity": "sha512-dP1jyl0n1Dx7GYDb0bcsbv9Yx0oBRLhUtSyNFRIOsSzV9MvCVbua/3WP6YUP2JptAU5BciEWGInV+hFjUuYLUw==",
+ "license": "MIT",
+ "dependencies": {
+ "postal-mime": "2.7.5",
+ "standardwebhooks": "1.0.0"
+ },
+ "engines": {
+ "node": ">=20"
+ },
+ "peerDependencies": {
+ "@react-email/render": "*"
+ },
+ "peerDependenciesMeta": {
+ "@react-email/render": {
+ "optional": true
+ }
+ }
+ },
"node_modules/resolve-pkg-maps": {
"version": "1.0.0",
"resolved": "https://registry.npmjs.org/resolve-pkg-maps/-/resolve-pkg-maps-1.0.0.tgz",
@@ -4005,6 +4045,16 @@
"url": "https://github.com/sponsors/wooorm"
}
},
+ "node_modules/standardwebhooks": {
+ "version": "1.0.0",
+ "resolved": "https://registry.npmjs.org/standardwebhooks/-/standardwebhooks-1.0.0.tgz",
+ "integrity": "sha512-BbHGOQK9olHPMvQNHWul6MYlrRTAOKn03rOe4A8O3CLWhNf4YHBqq2HJKKC+sfqpxiBY52pNeesD6jIiLDz8jg==",
+ "license": "MIT",
+ "dependencies": {
+ "@stablelib/base64": "^1.0.0",
+ "fast-sha256": "^1.3.0"
+ }
+ },
"node_modules/statuses": {
"version": "2.0.2",
"resolved": "https://registry.npmjs.org/statuses/-/statuses-2.0.2.tgz",
diff --git a/package.json b/package.json
index fe43ac9..2619d46 100644
--- a/package.json
+++ b/package.json
@@ -15,6 +15,7 @@
"@astrojs/node": "^11.0.2",
"@astrojs/sitemap": "^3.7.3",
"astro": "^7.1.4",
- "dotenv": "^17.4.2"
+ "dotenv": "^17.4.2",
+ "resend": "^6.22.0"
}
}
diff --git a/src/components/Header.astro b/src/components/Header.astro
index 0d12057..36373bd 100644
--- a/src/components/Header.astro
+++ b/src/components/Header.astro
@@ -34,6 +34,7 @@ const projects = [
+ Contact
About
diff --git a/src/pages/about.astro b/src/pages/about.astro
index cd8d8cb..4386270 100644
--- a/src/pages/about.astro
+++ b/src/pages/about.astro
@@ -10,7 +10,7 @@ import noelleImage2 from '../assets/images/brand-package/noelle-paris2.jpg';
diff --git a/src/pages/api/send-message.ts b/src/pages/api/send-message.ts
new file mode 100644
index 0000000..aa40b00
--- /dev/null
+++ b/src/pages/api/send-message.ts
@@ -0,0 +1,39 @@
+import type { APIRoute } from 'astro';
+import { Resend } from 'resend';
+
+const resend = new Resend(import.meta.env.RESEND_API_KEY);
+
+export const POST: APIRoute = async ({ request }) => {
+ try {
+ const data = await request.json();
+ const { name, email, phone, message } = data;
+
+ if (!name || !email || !message) {
+ return new Response(
+ JSON.stringify({ message: 'Missing required fields' }),
+ { status: 400, headers: { 'Content-Type': 'application/json' } }
+ );
+ }
+
+ // Send email via Resend
+ await resend.emails.send({
+ from: 'Noelle Paris Design Contact Form ', // Update to your domain once verified in Resend
+ //to: 'github@juchatz.com',
+ to: 'inquiries@noelleparisdesign.com',
+ replyTo: email,
+ subject: `New Inquiry from ${name}`,
+ text: `Name: ${name}\nEmail: ${email}\nPhone: ${phone || 'N/A'}\n\nMessage:\n${message}`,
+ });
+
+ return new Response(
+ JSON.stringify({ message: 'Message sent successfully' }),
+ { status: 200, headers: { 'Content-Type': 'application/json' } }
+ );
+ } catch (error) {
+ console.error('Email error:', error);
+ return new Response(
+ JSON.stringify({ message: 'Error sending email' }),
+ { status: 500, headers: { 'Content-Type': 'application/json' } }
+ );
+ }
+};
diff --git a/src/pages/contact.astro b/src/pages/contact.astro
new file mode 100644
index 0000000..cebb850
--- /dev/null
+++ b/src/pages/contact.astro
@@ -0,0 +1,194 @@
+---
+import Layout from '../layouts/Layout.astro';
+---
+
+
+
+
+
+
+
+
+
+
+
+