implemented contact form using Node.js and Postmark.
This commit is contained in:
@@ -25,11 +25,12 @@ Bug Fixes:
|
|||||||
|
|
||||||
Week 2: 24/12/24 - 30/12/24 (Adding key information)
|
Week 2: 24/12/24 - 30/12/24 (Adding key information)
|
||||||
|
|
||||||
- [ ] Resume page (HTML and PDF options).
|
- [x] Resume page (HTML and PDF options).
|
||||||
- [ ] Convert SLCC e-portfolio to my own website
|
- [ ] Convert SLCC e-portfolio to my own website
|
||||||
- [ ] Meet required pages and content
|
- [ ] Meet required pages and content
|
||||||
- [ ] Embed PDF course reflections/signature assignments.
|
- [ ] Embed PDF course reflections/signature assignments.
|
||||||
- [ ] Contact page.
|
- [x] Contact page.
|
||||||
|
- [ ] Implement MailTrap for sending me automated emails when there is a contact form submission.
|
||||||
|
|
||||||
Week 3: 31/12/24 - 6/12/24
|
Week 3: 31/12/24 - 6/12/24
|
||||||
|
|
||||||
@@ -41,5 +42,3 @@ Week 3: 31/12/24 - 6/12/24
|
|||||||
- [ ] Separate user for self-hosting, with reduced permissions.
|
- [ ] Separate user for self-hosting, with reduced permissions.
|
||||||
- [ ] Dockerize
|
- [ ] Dockerize
|
||||||
- [ ] Blog page (stretch goal).
|
- [ ] Blog page (stretch goal).
|
||||||
|
|
||||||
Don't be the person who informs people against their will.
|
|
||||||
|
|||||||
Generated
+866
-2
File diff suppressed because it is too large
Load Diff
@@ -4,6 +4,9 @@
|
|||||||
"@fortawesome/free-brands-svg-icons": "^6.7.2",
|
"@fortawesome/free-brands-svg-icons": "^6.7.2",
|
||||||
"@fortawesome/free-solid-svg-icons": "^6.7.2",
|
"@fortawesome/free-solid-svg-icons": "^6.7.2",
|
||||||
"@fortawesome/react-fontawesome": "^0.2.2",
|
"@fortawesome/react-fontawesome": "^0.2.2",
|
||||||
|
"cors": "^2.8.5",
|
||||||
|
"express": "^4.21.2",
|
||||||
|
"postmark": "^4.0.5",
|
||||||
"react": "^18.2.0",
|
"react": "^18.2.0",
|
||||||
"react-dom": "^18.2.0",
|
"react-dom": "^18.2.0",
|
||||||
"react-pdf": "^9.2.1",
|
"react-pdf": "^9.2.1",
|
||||||
|
|||||||
+26
-9
@@ -1,5 +1,4 @@
|
|||||||
import React, { useState } from "react";
|
import React, { useState } from "react";
|
||||||
import { FontAwesomeIcon } from "@fortawesome/react-fontawesome";
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* TODO: Add email validation function.
|
* TODO: Add email validation function.
|
||||||
@@ -7,10 +6,12 @@ import { FontAwesomeIcon } from "@fortawesome/react-fontawesome";
|
|||||||
export default function Contact() {
|
export default function Contact() {
|
||||||
const [formData, setFormData] = useState({
|
const [formData, setFormData] = useState({
|
||||||
name: "",
|
name: "",
|
||||||
email: "",
|
emailAddress: "",
|
||||||
message: "",
|
message: "",
|
||||||
});
|
});
|
||||||
|
|
||||||
|
const [isSubmitted, setIsSubmitted] = useState(false);
|
||||||
|
|
||||||
const handleChange = (event) => {
|
const handleChange = (event) => {
|
||||||
setFormData({
|
setFormData({
|
||||||
...formData,
|
...formData,
|
||||||
@@ -18,15 +19,30 @@ export default function Contact() {
|
|||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
const handleSubmit = (event) => {
|
const handleSubmit = async (event) => {
|
||||||
event.preventDefault();
|
event.preventDefault(); // Prevent default form submission behavior
|
||||||
// Handle form submission here, e.g., send data to server
|
setIsSubmitted(true);
|
||||||
console.log(formData);
|
|
||||||
|
try {
|
||||||
|
const response = await fetch("http://localhost:3001/api/send-email", {
|
||||||
|
method: "POST", // Use POST for sending data
|
||||||
|
headers: { "Content-Type": "application/json" }, // Set the content type
|
||||||
|
body: JSON.stringify(formData), // Convert the JS object to JSON string
|
||||||
|
});
|
||||||
|
const json = await response.json();
|
||||||
|
setData(json);
|
||||||
|
} catch (error) {
|
||||||
|
console.error("Error fetching data:", error);
|
||||||
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div id="contact-section">
|
<div id="contact-section">
|
||||||
<h1>Contact Me</h1>
|
<h1>Contact Me</h1>
|
||||||
|
<div id="form-container"></div>
|
||||||
|
{isSubmitted ? (
|
||||||
|
<p>Thanks for reaching out! I'll get in touch shortly.</p>
|
||||||
|
) : (
|
||||||
<form onSubmit={handleSubmit}>
|
<form onSubmit={handleSubmit}>
|
||||||
<div className="row">
|
<div className="row">
|
||||||
<input
|
<input
|
||||||
@@ -42,9 +58,9 @@ export default function Contact() {
|
|||||||
className="shadow"
|
className="shadow"
|
||||||
placeholder="example@email.com"
|
placeholder="example@email.com"
|
||||||
type="email"
|
type="email"
|
||||||
id="email"
|
id="emailAddress"
|
||||||
name="email"
|
name="emailAddress"
|
||||||
value={formData.email}
|
value={formData.emailAddress}
|
||||||
onChange={handleChange}
|
onChange={handleChange}
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
@@ -65,6 +81,7 @@ export default function Contact() {
|
|||||||
</button>
|
</button>
|
||||||
</div>
|
</div>
|
||||||
</form>
|
</form>
|
||||||
|
)}
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|||||||
+6
-10
@@ -63,6 +63,11 @@ h4 {
|
|||||||
font-size: clamp(1rem, 1.5vw, 1.25rem);
|
font-size: clamp(1rem, 1.5vw, 1.25rem);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
p {
|
||||||
|
font-size: clamp(1rem, 1.5rem, 2rem);
|
||||||
|
color: #ebd2ff;
|
||||||
|
}
|
||||||
|
|
||||||
.material-icons {
|
.material-icons {
|
||||||
font-variation-settings:
|
font-variation-settings:
|
||||||
"FILL" 0,
|
"FILL" 0,
|
||||||
@@ -191,23 +196,16 @@ h4 {
|
|||||||
align-items: center;
|
align-items: center;
|
||||||
justify-content: center;
|
justify-content: center;
|
||||||
width: var(--width);
|
width: var(--width);
|
||||||
aspect-ratio: 1/1;
|
|
||||||
margin: 0 auto;
|
margin: 0 auto;
|
||||||
border-radius: 50%;
|
|
||||||
overflow: hidden;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#header-image img {
|
#header-image img {
|
||||||
display: block;
|
|
||||||
width: calc(var(--width) / 2);
|
width: calc(var(--width) / 2);
|
||||||
filter: drop-shadow(5px 6px 5px rgba(36, 36, 42, 0.5));
|
filter: drop-shadow(5px 6px 5px rgba(36, 36, 42, 0.5));
|
||||||
user-select: none;
|
user-select: none;
|
||||||
z-index: 1;
|
z-index: 1;
|
||||||
border-radius: 50%;
|
|
||||||
}
|
|
||||||
|
|
||||||
#header-image img {
|
|
||||||
animation: imgMoveDown 0.8s ease-in forwards 0.2s;
|
animation: imgMoveDown 0.8s ease-in forwards 0.2s;
|
||||||
|
border-radius: 50%;
|
||||||
}
|
}
|
||||||
|
|
||||||
#skill-cards {
|
#skill-cards {
|
||||||
@@ -350,8 +348,6 @@ h4 {
|
|||||||
flex-direction: column;
|
flex-direction: column;
|
||||||
justify-content: center;
|
justify-content: center;
|
||||||
align-items: center;
|
align-items: center;
|
||||||
font-size: clamp(1rem, 1.5rem, 2rem);
|
|
||||||
color: #ebd2ff;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
.row {
|
.row {
|
||||||
|
|||||||
@@ -0,0 +1,29 @@
|
|||||||
|
import express from "express";
|
||||||
|
import cors from "cors";
|
||||||
|
import postmark from "postmark";
|
||||||
|
const app = express();
|
||||||
|
const port = 3001; // Example port
|
||||||
|
const client = new postmark.ServerClient(
|
||||||
|
"d15a4575-17f1-4ead-8e53-69eb2faea982",
|
||||||
|
);
|
||||||
|
|
||||||
|
app.use(cors());
|
||||||
|
app.use(express.json()); // Parse incoming JSON data
|
||||||
|
|
||||||
|
// Example API endpoint
|
||||||
|
app.post("/api/send-email", (req, res) => {
|
||||||
|
const email = {
|
||||||
|
From: "contact@joshashton.dev",
|
||||||
|
To: "me@joshashton.dev",
|
||||||
|
Subject: req.body.name + " - " + req.body.emailAddress,
|
||||||
|
TextBody: req.body.message,
|
||||||
|
MessageStream: "outbound",
|
||||||
|
};
|
||||||
|
|
||||||
|
client.sendEmail(email);
|
||||||
|
res.json({ text: "email sent!" });
|
||||||
|
});
|
||||||
|
|
||||||
|
app.listen(port, () => {
|
||||||
|
console.log(`Server listening on port ${port}`);
|
||||||
|
});
|
||||||
Reference in New Issue
Block a user