Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Feat: Contact Us endpoints Api #163

Merged
merged 1 commit into from
Jul 22, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
32 changes: 32 additions & 0 deletions src/__test__/contact.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
import request from 'supertest';
import express from 'express';
import { handleContact } from '../controller/contactController'; // Adjust the import path accordingly

const app = express();
app.use(express.json());
app.post('/api/v1/contact', handleContact);

describe('POST /api/v1/contact', () => {
beforeEach(() => {
process.env.NODE_ENV = 'test'; // Ensure we're in test mode
});

it('should return 400 if name, email, or message is missing', async () => {
await request(app)
.post('/api/v1/contact')
.send({ email: 'test@example.com', message: 'Hello' })
.expect(400)
.expect({ error: 'Name, email, and message are required' });
});

it('should send feedback successfully', async () => {
await request(app)
.post('/api/v1/contact')
.send({ name: 'John Doe', email: 'john@example.com', message: 'Hello' })
.expect(200)
.expect({ message: 'Feedback sent successfully' });
});

// Since we're not testing the actual email sending functionality here,
// there's no need for a test that simulates failure to send feedback.
});
26 changes: 26 additions & 0 deletions src/controller/contactController.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
import { Request, Response } from 'express';
import sendEmail from '../emails/contact';

export const handleContact = async (req: Request, res: Response) => {
const { name, email, phone, message } = req.body;

if (!name || !email || !message) {
return res
.status(400)
.json({ error: 'Name, email, and message are required' });
}

try {
if (process.env.NODE_ENV !== 'test') {
await sendEmail('contact', 'bentopride@gmail.com', {
name,
email,
phone,
message,
});
}
res.status(200).json({ message: 'Feedback sent successfully' });
} catch (error) {
res.status(500).json({ error: 'Failed to send feedback' });
}
};
68 changes: 68 additions & 0 deletions src/docs/contact.doc.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,68 @@
/**
* @swagger
* tags:
* name: Contact
* description: Contact management
*/
/**
* @swagger
* /api/v1/contact:
* post:
* summary: Handle contact form submissions
* description: This endpoint handles contact form submissions and sends the data via email to administrators.
* tags: [Contact]
* requestBody:
* required: true
* content:
* application/json:
* schema:
* type: object
* properties:
* name:
* type: string
* example: John Doe
* email:
* type: string
* example: john.doe@example.com
* phone:
* type: string
* example: 123-456-7890
* message:
* type: string
* example: This is a test message.
* required:
* - name
* - email
* - message
* responses:
* 200:
* description: Feedback sent successfully
* content:
* application/json:
* schema:
* type: object
* properties:
* message:
* type: string
* example: Feedback sent successfully
* 400:
* description: Name, email, and message are required
* content:
* application/json:
* schema:
* type: object
* properties:
* error:
* type: string
* example: Name, email, and message are required
* 500:
* description: Failed to send feedback
* content:
* application/json:
* schema:
* type: object
* properties:
* error:
* type: string
* example: Failed to send feedback
*/
62 changes: 62 additions & 0 deletions src/emails/contact.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,62 @@
import axios from 'axios';
import handlebars from 'handlebars';
import fs from 'fs';
type EmailType = 'contact';
type Data = {
name: string;
phone: string;
email: string;
message: string;
};
/**
* Sends an email of the specified type to the recipient using the provided data.
*
* @param emailType - The type of email to send. Must be either "confirm" or "reset".
* @param recipient - The email address of the recipient.
* @param data - The data to be used for generating the email content. A name and link are required.
* @returns A Promise that resolves to the response from the email service.
* @throws An error if there is an issue sending the email.
*/
async function sendEmail(emailType: EmailType, recipient: string, data: Data) {
const templatePath = './src/emails/templates/contact.html';
try {
// Read the Handlebars template file
const templateFile = fs.readFileSync(templatePath, 'utf-8');

// Compile the template
const template = handlebars.compile(templateFile);

// Generate the HTML content using the template and data
const html = template(data);

// Send the Email

const domain = process.env.MAILGUN_DOMAIN;
const key = process.env.MAILGUN_TOKEN as string;
const body = {
from: `Dynamites Account Team <info@${domain}>`,
to: [recipient],
subject: 'New Contact',
html: html,
};
const mailgunResponse = await axios.post(
`https://api.mailgun.net/v3/${domain}/messages`,
body,
{
auth: {
username: 'api',
password: key,
},
headers: {
'Content-Type': 'multipart/form-data',
},
}
);

return mailgunResponse;
} catch (error) {
throw new Error(`Error sending email: ${error}`);
}
}

export default sendEmail;
2 changes: 1 addition & 1 deletion src/emails/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -57,4 +57,4 @@ async function sendEmail(emailType: EmailType, recipient: string, data: Data) {
}
}

export default sendEmail;
export default sendEmail;
133 changes: 133 additions & 0 deletions src/emails/templates/contact.html
Original file line number Diff line number Diff line change
@@ -0,0 +1,133 @@
<!doctype html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<link
href="https://fonts.googleapis.com/css2?family=Open+Sans:wght@400;600&display=swap"
rel="stylesheet"
/>
<style>
body {
font-family: 'Open Sans', sans-serif;
background-color: #f7fafc;
}

.container {
min-height: 100vh;
width: 100vw;
display: flex;
align-items: center;
justify-content: center;
padding: 12px;
box-sizing: border-box;
background-color: #f7fafc;
}

.card {
max-width: 720px;
width: 100%;
background-color: #ffffff;
padding: 24px;
border-radius: 8px;
box-shadow: 0 4px 6px rgba(0, 0, 0, 0.1);
}

.title {
margin-top: 24px;
margin-bottom: 16px;
font-size: 24px;
font-weight: bold;
color: #1a202c;
text-align: center;
}

.message {
margin-bottom: 16px;
font-size: 14px;
color: #4a5568;
text-align: center;
}

.button {
display: block;
width: 100%;
padding: 12px;
border: none;
border-radius: 4px;
font-size: 14px;
font-weight: bold;
color: #f7fafc;
background-color: #4c51bf;
text-align: center;
text-decoration: none;
cursor: pointer;
}

.button:hover {
background-color: #434190;
}

.link {
color: #4c51bf;
text-decoration: none;
}

.link:hover {
color: #434190;
}
table {
width: 100%;
padding: 0;
border-width: 0;
}
th,
td {
padding: 0;
margin-bottom: 32px;
border-width: 0;
text-align: left;
font-size: 17px;
}
th {
width: 30%;
}
td {
width: 70%;
}
</style>
</head>
<body>
<div class="container">
<div class="card">
<div class="title">New Message</div>
<p class="message">hello Admin,</p>
<p class="message">
you have new message from {{name}}. Here are the message details
</p>
<div>
<table>
<tr>
<th>Full Name:</th>
<td>{{name}}</td>
</tr>
<tr>
<th>Email:</th>
<td>{{email}}</td>
</tr>
<tr>
<th>Phone Number:</th>
<td>{{phone}}</td>
</tr>
<tr>
<th>Message:</th>
<td>
<p>{{message}}</p>
</td>
</tr>
</table>
</div>
</div>
</div>
</body>
</html>
9 changes: 9 additions & 0 deletions src/routes/contactRoutes.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
import { Router } from 'express';

import { handleContact } from '../controller/contactController';

const contactRoutes = Router();

contactRoutes.route('/').post(handleContact);

export default contactRoutes;
6 changes: 4 additions & 2 deletions src/routes/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,8 @@ import couponRouter from './couponRoute';
import chekoutRoutes from './checkoutRoutes';
import reviewRoute from './reviewRoutes';
import orderRoutes from './orderRoutes';
import noticificationRoute from './notificationRoutes'
import noticificationRoute from './notificationRoutes';
import contactRoutes from './contactRoutes';
const router = Router();

router.use('/user', userRouter);
Expand All @@ -22,5 +23,6 @@ router.use('/coupons', couponRouter);
router.use('/checkout', chekoutRoutes);
router.use('/review', reviewRoute);
router.use('/order', orderRoutes);
router.use('/notification',noticificationRoute)
router.use('/notification', noticificationRoute);
router.use('/contact', contactRoutes);
export default router;
Loading