diff --git a/web/components/InputField/ContactInputField.js b/web/components/InputField/ContactInputField.js
new file mode 100644
index 0000000..2395526
--- /dev/null
+++ b/web/components/InputField/ContactInputField.js
@@ -0,0 +1,52 @@
+import React from 'react';
+import { Field } from 'formik';
+import {
+ FormControl,
+ FormLabel,
+ FormErrorMessage,
+ Input,
+ Textarea,
+ IconButton,
+} from '@chakra-ui/react';
+import InputPopover from './InputPopover';
+import { RiErrorWarningFill } from 'react-icons/ri';
+
+function ContactInputField({ type, label, name, error, touched, tag }) {
+ return (
+
+ {({ field }) => (
+
+
+ {label}
+ {error && touched ? (
+ {error} }
+ trigger={
+
+
+
+ }
+ />
+ ) : (
+ ''
+ )}
+
+ {tag === 'Input' ? (
+
+ ) : (
+
+ )}
+
+ )}
+
+ );
+}
+
+export default ContactInputField;
diff --git a/web/components/InputField/InputPopover.js b/web/components/InputField/InputPopover.js
new file mode 100644
index 0000000..8a427f9
--- /dev/null
+++ b/web/components/InputField/InputPopover.js
@@ -0,0 +1,29 @@
+import React from 'react';
+import {
+ Popover,
+ PopoverTrigger,
+ PopoverContent,
+ PopoverHeader,
+ PopoverBody,
+ PopoverArrow,
+ PopoverCloseButton,
+ Portal,
+} from '@chakra-ui/react';
+
+function InputPopover({ header, trigger, content, openOn }) {
+ return (
+
+ {trigger}
+
+
+
+ {header}
+
+ {content}
+
+
+
+ );
+}
+
+export default InputPopover;
diff --git a/web/config/db/migrations.js b/web/config/db/migrations.js
index 67e2ad4..0debd0d 100644
--- a/web/config/db/migrations.js
+++ b/web/config/db/migrations.js
@@ -54,6 +54,11 @@ const migrations = [
scriptDate: '2022-01-08',
sql: 'ALTER TABLE indeedjobs ADD fullDesc TEXT NOT NULL;',
},
+ {
+ scriptName: 'create-contacts-table',
+ scriptDate: '2022-02-08',
+ sql: 'CREATE TABLE contacts(id INT NOT NULL AUTO_INCREMENT PRIMARY KEY, email VARCHAR(128) NOT NULL, name VARCHAR(32) NOT NULL, description VARCHAR(1000) NOT NULL);',
+ },
];
module.exports = migrations;
diff --git a/web/pages/api/contacts/index.js b/web/pages/api/contacts/index.js
new file mode 100644
index 0000000..7f2b39c
--- /dev/null
+++ b/web/pages/api/contacts/index.js
@@ -0,0 +1,18 @@
+import nextConnect from 'next-connect';
+import { query } from '../../../utils/query';
+
+const handler = nextConnect().post(async (req, res) => {
+ const { email, name, description } = await req.body;
+ try {
+ await query(`INSERT INTO contacts(email, name, description) VALUES(?, ?, ?)`, [
+ email,
+ name,
+ description,
+ ]);
+ return res.status(200).json({ message: 'Message sent!' });
+ } catch (e) {
+ res.status(500).json({ message: e.message });
+ }
+});
+
+export default handler;
diff --git a/web/pages/contact.js b/web/pages/contact.js
new file mode 100644
index 0000000..b33ced7
--- /dev/null
+++ b/web/pages/contact.js
@@ -0,0 +1,140 @@
+import React, { useState } from 'react';
+import {
+ FormControl,
+ Button,
+ Flex,
+ Heading,
+ VStack,
+ Box,
+ Alert,
+ AlertIcon,
+ AlertDescription,
+} from '@chakra-ui/react';
+import { Formik, Form } from 'formik';
+import * as Yup from 'yup';
+import ContactInputField from '../components/InputField/ContactInputField';
+
+const initialValues = {
+ email: '',
+ name: '',
+ description: '',
+};
+
+function Contact() {
+ const [response, setResponse] = useState();
+
+ const contactSchema = Yup.object().shape({
+ email: Yup.string().required('Required'),
+ name: Yup.string().min(2, 'Too short').required('Required'),
+ description: Yup.string().max(1000, 'Too long').required('Required'),
+ });
+
+ async function onContacts(values) {
+ const body = {
+ email: values.email,
+ name: values.name,
+ description: values.description,
+ };
+
+ let res = await fetch('./api/contacts', {
+ method: 'POST',
+ headers: { 'Content-Type': 'application/json' },
+ body: JSON.stringify(body),
+ });
+
+ res = await res.json();
+ setResponse(res);
+ initialValues = { initialValues };
+ }
+ return (
+ <>
+
+
+ Contact Us
+ {response && response.message && (
+
+
+ {response.message}
+
+ )}
+
+
+ {
+ await onContacts(values);
+ actions.setSubmitting(false);
+ actions.resetForm({ initialValues });
+ }}
+ >
+ {({ errors, touched, isSubmitting, isValid }) => (
+
+ )}
+
+
+
+
+ >
+ );
+}
+
+export default Contact;