diff --git a/.gitignore b/.gitignore index 55175ef..56f72c6 100644 --- a/.gitignore +++ b/.gitignore @@ -27,6 +27,7 @@ yarn-error.log* # local env files .env*.local +.env # vercel .vercel diff --git a/app/api/contact/route.js b/app/api/contact/route.js new file mode 100644 index 0000000..8b34c11 --- /dev/null +++ b/app/api/contact/route.js @@ -0,0 +1,36 @@ +import { NextRequest, NextResponse } from 'next/server'; +import nodemailer from 'nodemailer'; + +export async function POST(request) { + require('dotenv').config() + + let body = await request.formData(); + let visitorname = body.get('visitorname'); + let email = body.get('email'); + let message = body.get('message'); + + const transporter = nodemailer.createTransport({ + port: 465, + host: "smtp.gmail.com", + auth: { + user: process.env.MY_EMAIL, + pass: process.env.MY_PASSWORD, + }, + secure: true, + }) + const mailData = { + from: email, + to: 'misterjulius17@gmail.com', + subject: `Message From ${visitorname}`, + text: message + " | Sent from: " + email, + html: `
${message}

Sent from: + ${email}

` + } + transporter.sendMail(mailData, function (err, info) { + if(err) + console.log(err) + else + console.log(info) + }) + return NextResponse.json({message: 'all good'}) + } diff --git a/app/api/newsletter/route.js b/app/api/newsletter/route.js new file mode 100644 index 0000000..eb52218 --- /dev/null +++ b/app/api/newsletter/route.js @@ -0,0 +1,34 @@ +import { NextRequest, NextResponse } from 'next/server'; +import nodemailer from 'nodemailer'; + +export async function POST(request) { + require('dotenv').config() + + let body = await request.formData(); + let email = body.get('email'); + + const transporter = nodemailer.createTransport({ + port: 465, + host: "smtp.gmail.com", + auth: { + user: process.env.MY_EMAIL, + pass: process.env.MY_PASSWORD, + }, + secure: true, + }) + const mailData = { + from: email, + to: 'misterjulius17@gmail.com', + subject: `Newsletter Request From ${email}`, + text: "Hello PyCon Uganda, kindly add me: " + email, + html: `

Hello PyCon Uganda, kindly add me: + ${email}

` + } + transporter.sendMail(mailData, function (err, info) { + if(err) + console.log(err) + else + console.log(info) + }) + return NextResponse.json({message: 'all good'}) + } diff --git a/app/api/sponsor/route.js b/app/api/sponsor/route.js new file mode 100644 index 0000000..e5f2a3c --- /dev/null +++ b/app/api/sponsor/route.js @@ -0,0 +1,36 @@ +import { NextRequest, NextResponse } from 'next/server'; +import nodemailer from 'nodemailer'; + +export async function POST(request) { + require('dotenv').config() + + let body = await request.formData(); + let visitorname = body.get('visitorname'); + let email = body.get('email'); + let message = body.get('message'); + + const transporter = nodemailer.createTransport({ + port: 465, + host: "smtp.gmail.com", + auth: { + user: process.env.MY_EMAIL, + pass: process.env.MY_PASSWORD, + }, + secure: true, + }) + const mailData = { + from: email, + to: 'misterjulius17@gmail.com', + subject: `Sponsorship Request From ${visitorname}`, + text: message + " | Sent from: " + email, + html: `
${message}

Sent from: + ${email}

` + } + transporter.sendMail(mailData, function (err, info) { + if(err) + console.log(err) + else + console.log(info) + }) + return NextResponse.json({message: 'all good'}) + } diff --git a/app/contact/page.jsx b/app/contact/page.jsx index 75e10b5..78f6b0a 100644 --- a/app/contact/page.jsx +++ b/app/contact/page.jsx @@ -1,6 +1,42 @@ -import { PYCONUG_EMAIL } from "@/utils/constants"; +'use client'; + +import { useState } from "react"; +import FormData from 'form-data'; export default function ContactPage() { + const form = new FormData(); + const [formData, setFormData] = useState(form); + const [successMessage, setSuccessMessage] = useState(''); + const [errorMessage, setErrorMessage] = useState(''); + + const handleFormSubmit = async (event) => { + event.preventDefault(); + + formData.append('visitorname', event.target.visitorname.value); + formData.append('email', event.target.email.value); + formData.append('message',event.target.message.value); + + // Display formData in console (for debugging) + //console.log([...formData]); + + try { + const response = await fetch('/api/contact', { + method: 'POST', + body: formData, + }); + if (response.ok) { + setSuccessMessage('Your message has been received, we shall reach out soon!'); + setErrorMessage(''); + } else { + setErrorMessage('Something went wrong, please try again'); + setSuccessMessage(''); + } + } catch (err) { + setErrorMessage('An error occurred while submitting the form'); + setSuccessMessage(''); + } + }; + return ( <>
@@ -11,7 +47,7 @@ export default function ContactPage() {

Interested in sponsoring or have a question? We would love to hear - from you. + from you

@@ -20,21 +56,20 @@ export default function ContactPage() {
@@ -49,6 +84,7 @@ export default function ContactPage() { @@ -63,13 +99,14 @@ export default function ContactPage() {
diff --git a/app/sponsors/page.jsx b/app/sponsors/page.jsx index ffa0c3e..0592132 100644 --- a/app/sponsors/page.jsx +++ b/app/sponsors/page.jsx @@ -1,6 +1,41 @@ -import { PYCONUG_EMAIL } from "@/utils/constants"; +'use client'; + +import { useState } from "react"; +import FormData from 'form-data'; export default function SponsorsPage() { + const form = new FormData(); + const [formData, setFormData] = useState(form); + const [successMessage, setSuccessMessage] = useState(''); + const [errorMessage, setErrorMessage] = useState(''); + + const handleFormSubmit = async (event) => { + event.preventDefault(); + + formData.append('visitorname', event.target.visitorname.value); + formData.append('email', event.target.email.value); + formData.append('message',event.target.message.value); + + // Display formData in console (for debugging) + //console.log([...formData]); + + try { + const response = await fetch('/api/sponsor', { + method: 'POST', + body: formData, + }); + if (response.ok) { + setSuccessMessage('Thank you for showing interest to Sponsor PyCon Uganda 2023!'); + setErrorMessage(''); + } else { + setErrorMessage('Something went wrong, please try again'); + setSuccessMessage(''); + } + } catch (err) { + setErrorMessage('An error occurred while submitting the form'); + setSuccessMessage(''); + } + }; return ( <>
@@ -507,22 +542,25 @@ export default function SponsorsPage() {

Interested in sponsoring? We would love to hear from you.

+ + {successMessage &&

{successMessage}

} + {errorMessage &&

{errorMessage}

} +
@@ -537,6 +575,7 @@ export default function SponsorsPage() { @@ -551,13 +590,14 @@ export default function SponsorsPage() {
diff --git a/components/home/sections/contact.js b/components/home/sections/contact.js index d6d85bb..cfb4e0a 100644 --- a/components/home/sections/contact.js +++ b/components/home/sections/contact.js @@ -1,6 +1,42 @@ +'use client'; + +import { useState } from "react"; +import FormData from 'form-data'; import { PYCONUG_EMAIL } from "@/utils/constants"; export default function Contact() { + const form = new FormData(); + const [formData, setFormData] = useState(form); + const [successMessage, setSuccessMessage] = useState(''); + const [errorMessage, setErrorMessage] = useState(''); + + const handleFormSubmit = async (event) => { + event.preventDefault(); + + formData.append('visitorname', event.target.visitorname.value); + formData.append('email', event.target.email.value); + formData.append('message',event.target.message.value); + + // Display formData in console (for debugging) + //console.log([...formData]); + + try { + const response = await fetch('/api/contact', { + method: 'POST', + body: formData, + }); + if (response.ok) { + setSuccessMessage('Your message has been received, we shall reach out soon'); + setErrorMessage(''); + } else { + setErrorMessage('Something went wrong, please try again'); + setSuccessMessage(''); + } + } catch (err) { + setErrorMessage('An error occurred while submitting the form'); + setSuccessMessage(''); + } + }; return (
@@ -36,22 +72,25 @@ export default function Contact() {

Reach out to us in case of queries.

+ + {successMessage &&

{successMessage}

} + {errorMessage &&

{errorMessage}

} +
@@ -66,6 +105,7 @@ export default function Contact() { @@ -80,8 +120,9 @@ export default function Contact() {
diff --git a/package-lock.json b/package-lock.json index 81acfc9..a0b39a6 100644 --- a/package-lock.json +++ b/package-lock.json @@ -9,9 +9,12 @@ "version": "0.1.0", "dependencies": { "@headlessui/react": "^1.7.15", + "dotenv": "^16.3.1", "eslint": "8.35.0", "eslint-config-next": "13.2.1", + "form-data": "^4.0.0", "next": "13.2.1", + "nodemailer": "^6.9.4", "react": "18.2.0", "react-dom": "18.2.0", "swiper": "^10.0.4", @@ -1148,6 +1151,11 @@ "resolved": "https://registry.npmjs.org/ast-types-flow/-/ast-types-flow-0.0.7.tgz", "integrity": "sha512-eBvWn1lvIApYMhzQMsu9ciLfkBY499mFZlNqG+/9WR7PVlroQw0vG30cOQQbaKz3sCEc44TAOu2ykzqXSNnwag==" }, + "node_modules/asynckit": { + "version": "0.4.0", + "resolved": "https://registry.npmjs.org/asynckit/-/asynckit-0.4.0.tgz", + "integrity": "sha512-Oei9OH4tRh0YqU3GxhX79dM/mwVgvbZJaSNaRk+bshkj0S5cfHcgYakreBjrHwatXKbz+IoIdYLxrKim2MjW0Q==" + }, "node_modules/autoprefixer": { "version": "10.4.13", "resolved": "https://registry.npmjs.org/autoprefixer/-/autoprefixer-10.4.13.tgz", @@ -1560,6 +1568,17 @@ "integrity": "sha512-3tlv/dIP7FWvj3BsbHrGLJ6l/oKh1O3TcgBqMn+yyCagOxc23fyzDS6HypQbgxWbkpDnf52p1LuR4eWDQ/K9WQ==", "dev": true }, + "node_modules/combined-stream": { + "version": "1.0.8", + "resolved": "https://registry.npmjs.org/combined-stream/-/combined-stream-1.0.8.tgz", + "integrity": "sha512-FQN4MRfuJeHf7cBbBMJFXhKSDq+2kAArBlmRBvcvFE5BB1HZKXtSFASDhdlz9zOYwxh8lDdnvmMOe/+5cdoEdg==", + "dependencies": { + "delayed-stream": "~1.0.0" + }, + "engines": { + "node": ">= 0.8" + } + }, "node_modules/commander": { "version": "2.20.3", "resolved": "https://registry.npmjs.org/commander/-/commander-2.20.3.tgz", @@ -1740,6 +1759,14 @@ "integrity": "sha512-+uO4+qr7msjNNWKYPHqN/3+Dx3NFkmIzayk2L1MyZQlvgZb/J1A0fo410dpKrN2SnqFjt8n4JL8fDJE0wIgjFQ==", "dev": true }, + "node_modules/delayed-stream": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/delayed-stream/-/delayed-stream-1.0.0.tgz", + "integrity": "sha512-ZySD7Nf91aLB0RxL4KGrKHBXl7Eds1DAmEdcoVawXnLD7SDhpNgtuII2aAkg7a7QS41jxPSZ17p4VdGnMHk3MQ==", + "engines": { + "node": ">=0.4.0" + } + }, "node_modules/denque": { "version": "2.1.0", "resolved": "https://registry.npmjs.org/denque/-/denque-2.1.0.tgz", @@ -1820,6 +1847,17 @@ "node": ">=6.0.0" } }, + "node_modules/dotenv": { + "version": "16.3.1", + "resolved": "https://registry.npmjs.org/dotenv/-/dotenv-16.3.1.tgz", + "integrity": "sha512-IPzF4w4/Rd94bA9imS68tZBaYyBWSCE47V1RGuMrB94iyTOIEwRmVL2x/4An+6mETpLrKJ5hQkB8W4kFAadeIQ==", + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/motdotla/dotenv?sponsor=1" + } + }, "node_modules/electron-to-chromium": { "version": "1.4.313", "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.4.313.tgz", @@ -2593,6 +2631,19 @@ "is-callable": "^1.1.3" } }, + "node_modules/form-data": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/form-data/-/form-data-4.0.0.tgz", + "integrity": "sha512-ETEklSGi5t0QMZuiXoA/Q6vcnxcLQP5vdugSpuAyi6SVGi2clPPp+xgEhuMaHC+zGgn31Kd235W35f7Hykkaww==", + "dependencies": { + "asynckit": "^0.4.0", + "combined-stream": "^1.0.8", + "mime-types": "^2.1.12" + }, + "engines": { + "node": ">= 6" + } + }, "node_modules/fraction.js": { "version": "4.2.0", "resolved": "https://registry.npmjs.org/fraction.js/-/fraction.js-4.2.0.tgz", @@ -3679,6 +3730,25 @@ "node": ">=8.6" } }, + "node_modules/mime-db": { + "version": "1.52.0", + "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.52.0.tgz", + "integrity": "sha512-sPU4uV7dYlvtWJxwwxHD0PuihVNiE7TyAbQ5SWxDCB9mUYvOgroQOwYQQOKPJ8CIbE+1ETVlOoK1UC2nU3gYvg==", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/mime-types": { + "version": "2.1.35", + "resolved": "https://registry.npmjs.org/mime-types/-/mime-types-2.1.35.tgz", + "integrity": "sha512-ZDY+bPm5zTTF+YpCrAU9nK0UgICYPT0QtT1NZWFv4s++TNkcgVaT0g6+4R2uI4MjQjzysHB1zxuWL50hzaeXiw==", + "dependencies": { + "mime-db": "1.52.0" + }, + "engines": { + "node": ">= 0.6" + } + }, "node_modules/mimic-fn": { "version": "2.1.0", "resolved": "https://registry.npmjs.org/mimic-fn/-/mimic-fn-2.1.0.tgz", @@ -3947,6 +4017,14 @@ "url": "https://github.com/sponsors/antelle" } }, + "node_modules/nodemailer": { + "version": "6.9.4", + "resolved": "https://registry.npmjs.org/nodemailer/-/nodemailer-6.9.4.tgz", + "integrity": "sha512-CXjQvrQZV4+6X5wP6ZIgdehJamI63MFoYFGGPtHudWym9qaEHDNdPzaj5bfMCvxG1vhAileSWW90q7nL0N36mA==", + "engines": { + "node": ">=6.0.0" + } + }, "node_modules/normalize-path": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/normalize-path/-/normalize-path-3.0.0.tgz", @@ -6377,6 +6455,11 @@ "resolved": "https://registry.npmjs.org/ast-types-flow/-/ast-types-flow-0.0.7.tgz", "integrity": "sha512-eBvWn1lvIApYMhzQMsu9ciLfkBY499mFZlNqG+/9WR7PVlroQw0vG30cOQQbaKz3sCEc44TAOu2ykzqXSNnwag==" }, + "asynckit": { + "version": "0.4.0", + "resolved": "https://registry.npmjs.org/asynckit/-/asynckit-0.4.0.tgz", + "integrity": "sha512-Oei9OH4tRh0YqU3GxhX79dM/mwVgvbZJaSNaRk+bshkj0S5cfHcgYakreBjrHwatXKbz+IoIdYLxrKim2MjW0Q==" + }, "autoprefixer": { "version": "10.4.13", "resolved": "https://registry.npmjs.org/autoprefixer/-/autoprefixer-10.4.13.tgz", @@ -6656,6 +6739,14 @@ "integrity": "sha512-3tlv/dIP7FWvj3BsbHrGLJ6l/oKh1O3TcgBqMn+yyCagOxc23fyzDS6HypQbgxWbkpDnf52p1LuR4eWDQ/K9WQ==", "dev": true }, + "combined-stream": { + "version": "1.0.8", + "resolved": "https://registry.npmjs.org/combined-stream/-/combined-stream-1.0.8.tgz", + "integrity": "sha512-FQN4MRfuJeHf7cBbBMJFXhKSDq+2kAArBlmRBvcvFE5BB1HZKXtSFASDhdlz9zOYwxh8lDdnvmMOe/+5cdoEdg==", + "requires": { + "delayed-stream": "~1.0.0" + } + }, "commander": { "version": "2.20.3", "resolved": "https://registry.npmjs.org/commander/-/commander-2.20.3.tgz", @@ -6792,6 +6883,11 @@ "integrity": "sha512-+uO4+qr7msjNNWKYPHqN/3+Dx3NFkmIzayk2L1MyZQlvgZb/J1A0fo410dpKrN2SnqFjt8n4JL8fDJE0wIgjFQ==", "dev": true }, + "delayed-stream": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/delayed-stream/-/delayed-stream-1.0.0.tgz", + "integrity": "sha512-ZySD7Nf91aLB0RxL4KGrKHBXl7Eds1DAmEdcoVawXnLD7SDhpNgtuII2aAkg7a7QS41jxPSZ17p4VdGnMHk3MQ==" + }, "denque": { "version": "2.1.0", "resolved": "https://registry.npmjs.org/denque/-/denque-2.1.0.tgz", @@ -6854,6 +6950,11 @@ "esutils": "^2.0.2" } }, + "dotenv": { + "version": "16.3.1", + "resolved": "https://registry.npmjs.org/dotenv/-/dotenv-16.3.1.tgz", + "integrity": "sha512-IPzF4w4/Rd94bA9imS68tZBaYyBWSCE47V1RGuMrB94iyTOIEwRmVL2x/4An+6mETpLrKJ5hQkB8W4kFAadeIQ==" + }, "electron-to-chromium": { "version": "1.4.313", "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.4.313.tgz", @@ -7438,6 +7539,16 @@ "is-callable": "^1.1.3" } }, + "form-data": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/form-data/-/form-data-4.0.0.tgz", + "integrity": "sha512-ETEklSGi5t0QMZuiXoA/Q6vcnxcLQP5vdugSpuAyi6SVGi2clPPp+xgEhuMaHC+zGgn31Kd235W35f7Hykkaww==", + "requires": { + "asynckit": "^0.4.0", + "combined-stream": "^1.0.8", + "mime-types": "^2.1.12" + } + }, "fraction.js": { "version": "4.2.0", "resolved": "https://registry.npmjs.org/fraction.js/-/fraction.js-4.2.0.tgz", @@ -8207,6 +8318,19 @@ "picomatch": "^2.3.1" } }, + "mime-db": { + "version": "1.52.0", + "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.52.0.tgz", + "integrity": "sha512-sPU4uV7dYlvtWJxwwxHD0PuihVNiE7TyAbQ5SWxDCB9mUYvOgroQOwYQQOKPJ8CIbE+1ETVlOoK1UC2nU3gYvg==" + }, + "mime-types": { + "version": "2.1.35", + "resolved": "https://registry.npmjs.org/mime-types/-/mime-types-2.1.35.tgz", + "integrity": "sha512-ZDY+bPm5zTTF+YpCrAU9nK0UgICYPT0QtT1NZWFv4s++TNkcgVaT0g6+4R2uI4MjQjzysHB1zxuWL50hzaeXiw==", + "requires": { + "mime-db": "1.52.0" + } + }, "mimic-fn": { "version": "2.1.0", "resolved": "https://registry.npmjs.org/mimic-fn/-/mimic-fn-2.1.0.tgz", @@ -8377,6 +8501,11 @@ "integrity": "sha512-LN4fydt9TqhZhThkZIVQnF9cwjU3qmUH9h78Mx/K7d3VvfRqqwthLwJEUOEL0QPZ0XQmNN7be5Ggit5+4dq3Bw==", "dev": true }, + "nodemailer": { + "version": "6.9.4", + "resolved": "https://registry.npmjs.org/nodemailer/-/nodemailer-6.9.4.tgz", + "integrity": "sha512-CXjQvrQZV4+6X5wP6ZIgdehJamI63MFoYFGGPtHudWym9qaEHDNdPzaj5bfMCvxG1vhAileSWW90q7nL0N36mA==" + }, "normalize-path": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/normalize-path/-/normalize-path-3.0.0.tgz", diff --git a/package.json b/package.json index afeef9e..a473a41 100644 --- a/package.json +++ b/package.json @@ -10,9 +10,12 @@ }, "dependencies": { "@headlessui/react": "^1.7.15", + "dotenv": "^16.3.1", "eslint": "8.35.0", "eslint-config-next": "13.2.1", + "form-data": "^4.0.0", "next": "13.2.1", + "nodemailer": "^6.9.4", "react": "18.2.0", "react-dom": "18.2.0", "swiper": "^10.0.4",