mail format updated
This commit is contained in:
parent
2e3e78b632
commit
5eea071398
@ -1,39 +1,44 @@
|
|||||||
import { CakeOrder } from "../../models/maisondetreats/cakeOrder.model.js";
|
import { CakeOrder } from "../../models/maisondetreats/cakeOrder.model.js";
|
||||||
import { sendCakeOrderMail } from "../../utils/mailer.js";
|
import { sendCakeOrderMail } from "../../utils/mailer.js";
|
||||||
|
|
||||||
|
// POST → Create new cake order
|
||||||
export const createCakeOrder = async (req, res) => {
|
export const createCakeOrder = async (req, res) => {
|
||||||
try {
|
try {
|
||||||
const { order, email } = req.body;
|
const { order, email, totalPieces, totalPrice } = req.body;
|
||||||
|
|
||||||
if (!order || typeof order !== "object") {
|
if (!order || typeof order !== "object") {
|
||||||
return res.status(400).json({ message: "Order data is required" });
|
return res.status(400).json({ message: "Order data is required" });
|
||||||
}
|
|
||||||
|
|
||||||
const newOrder = await CakeOrder.create({ order, email });
|
|
||||||
|
|
||||||
// ✅ send confirmation email (non-blocking)
|
|
||||||
if (email) {
|
|
||||||
sendCakeOrderMail(email, order)
|
|
||||||
.then(() => console.log("Cake order email sent to", email))
|
|
||||||
.catch((err) => console.error("Email send failed:", err));
|
|
||||||
}
|
|
||||||
|
|
||||||
res.status(201).json({
|
|
||||||
message: "Cake order created successfully",
|
|
||||||
data: newOrder,
|
|
||||||
});
|
|
||||||
} catch (err) {
|
|
||||||
console.error("Error creating cake order:", err);
|
|
||||||
res.status(500).json({ message: "Server error", error: err.message });
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (!totalPieces || !totalPrice) {
|
||||||
|
return res.status(400).json({ message: "Total pieces and price are required" });
|
||||||
|
}
|
||||||
|
|
||||||
|
const newOrder = await CakeOrder.create({ order, email, totalPieces, totalPrice });
|
||||||
|
|
||||||
|
if (email) {
|
||||||
|
sendCakeOrderMail(email, order, totalPieces, totalPrice)
|
||||||
|
.then(() => console.log("Cake order email sent to", email))
|
||||||
|
.catch((err) => console.error("Email send failed:", err));
|
||||||
|
}
|
||||||
|
|
||||||
|
res.status(201).json({
|
||||||
|
message: "Cake order created successfully",
|
||||||
|
data: newOrder,
|
||||||
|
});
|
||||||
|
} catch (err) {
|
||||||
|
console.error("Error creating cake order:", err);
|
||||||
|
res.status(500).json({ message: "Server error", error: err.message });
|
||||||
|
}
|
||||||
};
|
};
|
||||||
// GET /api/cake-orders → List all orders
|
|
||||||
|
// GET → Fetch all cake orders
|
||||||
export const getAllCakeOrders = async (_req, res) => {
|
export const getAllCakeOrders = async (_req, res) => {
|
||||||
try {
|
try {
|
||||||
const orders = await CakeOrder.find().sort({ createdAt: -1 });
|
const orders = await CakeOrder.find().sort({ createdAt: -1 });
|
||||||
res.json({ data: orders });
|
res.json({ data: orders });
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
console.error("Error fetching cake orders:", err);
|
console.error("Error fetching cake orders:", err);
|
||||||
res.status(500).json({ message: "Server error", error: err.message });
|
res.status(500).json({ message: "Server error", error: err.message });
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|||||||
@ -1,16 +1,24 @@
|
|||||||
import mongoose from "mongoose";
|
import mongoose from "mongoose";
|
||||||
|
|
||||||
|
// Each flavour item schema
|
||||||
|
const FlavourSchema = new mongoose.Schema({
|
||||||
|
flavour: { type: String, required: true },
|
||||||
|
pieces: { type: Number, required: true },
|
||||||
|
unitPrice: { type: Number, required: true },
|
||||||
|
totalPrice: { type: Number, required: true },
|
||||||
|
});
|
||||||
|
|
||||||
|
// Main Cake Order schema
|
||||||
const CakeOrderSchema = new mongoose.Schema(
|
const CakeOrderSchema = new mongoose.Schema(
|
||||||
{
|
{
|
||||||
|
email: { type: String, required: true },
|
||||||
order: {
|
order: {
|
||||||
type: Object,
|
type: Map,
|
||||||
|
of: [FlavourSchema], // Each category has an array of flavour objects
|
||||||
required: true,
|
required: true,
|
||||||
// Example format:
|
|
||||||
// {
|
|
||||||
// "Mini Cakes": { "Thandai Cake": 1, "Mango Cardamom": 1 },
|
|
||||||
// "Mithai-Inspired Macarons": { "Mango macarons (pack of 6)": 1, "Pista (pack of 6)": 10 }
|
|
||||||
// }
|
|
||||||
},
|
},
|
||||||
|
totalPieces: { type: Number, required: true },
|
||||||
|
totalPrice: { type: Number, required: true },
|
||||||
},
|
},
|
||||||
{ timestamps: true }
|
{ timestamps: true }
|
||||||
);
|
);
|
||||||
|
|||||||
@ -62,8 +62,9 @@ export async function sendResetPasswordMail(email, token) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Send cake order email
|
|
||||||
export const sendCakeOrderMail = async (toEmail, orderData) => {
|
|
||||||
|
export const sendCakeOrderMail = async (email, order, totalPieces, totalPrice) => {
|
||||||
try {
|
try {
|
||||||
const transporter = nodemailer.createTransport({
|
const transporter = nodemailer.createTransport({
|
||||||
host: "mail.metatron-admin-backend.metatronhost.com",
|
host: "mail.metatron-admin-backend.metatronhost.com",
|
||||||
@ -78,35 +79,37 @@ export const sendCakeOrderMail = async (toEmail, orderData) => {
|
|||||||
|
|
||||||
// Build table rows
|
// Build table rows
|
||||||
let orderRows = "";
|
let orderRows = "";
|
||||||
Object.entries(orderData).forEach(([category, flavours]) => {
|
Object.entries(order).forEach(([category, items]) => {
|
||||||
Object.entries(flavours).forEach(([flavour, qty]) => {
|
items.forEach(({ flavour, pieces, unitPrice, totalPrice: lineTotal }) => {
|
||||||
orderRows += `
|
orderRows += `
|
||||||
<tr>
|
<tr>
|
||||||
<td style="padding:10px 12px;">${category}</td>
|
<td style="padding:10px 12px;">${category}</td>
|
||||||
<td style="padding:10px 12px;">${flavour}</td>
|
<td style="padding:10px 12px;">${flavour}</td>
|
||||||
<td style="padding:10px 12px;">${qty}</td>
|
<td style="padding:10px 12px;">${pieces}</td>
|
||||||
|
<td style="padding:10px 12px;">$${unitPrice.toFixed(2)}</td>
|
||||||
|
<td style="padding:10px 12px;">$${lineTotal.toFixed(2)}</td>
|
||||||
</tr>
|
</tr>
|
||||||
`;
|
`;
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
// HTML content with local images via cid
|
|
||||||
const htmlContent = `
|
const htmlContent = `
|
||||||
<div style="max-width:750px;margin:30px auto;background-color:#fff;border-radius:20px;overflow:hidden;font-family:'Segoe UI',Tahoma,sans-serif;color:#333;">
|
<div style="max-width:750px;margin:30px auto;background-color:#fff;border-radius:20px;overflow:hidden;font-family:'Segoe UI',Tahoma,sans-serif;color:#333;">
|
||||||
<table width="100%" cellpadding="0" cellspacing="0" style="background-color:#faf4f0;padding:15px 25px;">
|
<table width="100%" cellpadding="0" cellspacing="0" style="background-color:#faf4f0;padding:15px 25px;">
|
||||||
<tr>
|
<tr>
|
||||||
<td align="left">
|
<td align="left">
|
||||||
<img src="cid:logo" width="150" style="display:block;" />
|
<img src="cid:logo" width="150" style="display:block;" />
|
||||||
</td>
|
</td>
|
||||||
<td align="right" style="font-size:14px;color:#d72631;font-weight:600;">
|
<td align="right" style="font-size:14px;color:#d72631;font-weight:600;">
|
||||||
Order Date: ${new Date().toLocaleString()}
|
Order Date: ${new Date().toLocaleString()}
|
||||||
</td>
|
</td>
|
||||||
</tr>
|
</tr>
|
||||||
</table>
|
</table>
|
||||||
|
|
||||||
|
<div style="padding:15px 25px;text-align:center;">
|
||||||
<div style="text-align:center;padding:30px 0;">
|
<div style="text-align:center;padding:30px 0;">
|
||||||
<img src="cid:banner" style="width:100%;max-height:300px;object-fit:cover;border-radius:0 0 20px 20px;"/>
|
<img src="cid:banner" style="width:100%;max-height:300px;object-fit:cover;border-radius:0 0 20px 20px;"/>
|
||||||
</div>
|
</div>
|
||||||
<div style="padding:15px 25px;text-align:center;">
|
|
||||||
<h2 style="color:rgb(255 135 174);font-family:'Brush Script MT',cursive;font-size:22px;margin-bottom:12px;font-weight:700;">Order Details</h2>
|
<h2 style="color:rgb(255 135 174);font-family:'Brush Script MT',cursive;font-size:22px;margin-bottom:12px;font-weight:700;">Order Details</h2>
|
||||||
<table style="width:100%;border-collapse:collapse;font-size:14px;margin:0 auto 15px;">
|
<table style="width:100%;border-collapse:collapse;font-size:14px;margin:0 auto 15px;">
|
||||||
<thead>
|
<thead>
|
||||||
@ -114,14 +117,24 @@ export const sendCakeOrderMail = async (toEmail, orderData) => {
|
|||||||
<th style="padding:10px 12px;text-align:left;">Treats</th>
|
<th style="padding:10px 12px;text-align:left;">Treats</th>
|
||||||
<th style="padding:10px 12px;text-align:left;">Flavour</th>
|
<th style="padding:10px 12px;text-align:left;">Flavour</th>
|
||||||
<th style="padding:10px 12px;text-align:left;">Quantity</th>
|
<th style="padding:10px 12px;text-align:left;">Quantity</th>
|
||||||
|
<th style="padding:10px 12px;">Unit Price</th>
|
||||||
|
<th style="padding:10px 12px;">Total Price</th>
|
||||||
</tr>
|
</tr>
|
||||||
</thead>
|
</thead>
|
||||||
<tbody>
|
<tbody>
|
||||||
${orderRows}
|
${orderRows}
|
||||||
|
<tr style="font-weight:bold;background-color:#f8f8f8;">
|
||||||
|
<td colspan="2" style="text-align:right;padding:10px 12px;">Total Pieces</td>
|
||||||
|
<td style="padding:10px 12px;">${totalPieces}</td>
|
||||||
|
<td style="text-align:right;padding:10px 12px;">Total Price ($)</td>
|
||||||
|
<td style="padding:10px 12px;">$${totalPrice.toFixed(2)}</td>
|
||||||
|
</tr>
|
||||||
</tbody>
|
</tbody>
|
||||||
</table>
|
</table>
|
||||||
|
|
||||||
<a href="https://maisondetreats.com/" style="display:inline-block;background-color:#d72631;color:#fff;padding:10px 25px;border-radius:30px;text-decoration:none;font-weight:bold;margin-top:15px;">Visit Our Website</a>
|
<a href="https://maisondetreats.com/" style="display:inline-block;background-color:#d72631;color:#fff;padding:10px 25px;border-radius:30px;text-decoration:none;font-weight:bold;margin-top:15px;">Visit Our Website</a>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div style="text-align:center;padding:30px 0;background-color:#d72631;color:#fff;">
|
<div style="text-align:center;padding:30px 0;background-color:#d72631;color:#fff;">
|
||||||
<img src="cid:footer" style="width:100%;max-height:100px;object-fit:cover;"/>
|
<img src="cid:footer" style="width:100%;max-height:100px;object-fit:cover;"/>
|
||||||
</div>
|
</div>
|
||||||
@ -130,30 +143,18 @@ export const sendCakeOrderMail = async (toEmail, orderData) => {
|
|||||||
|
|
||||||
const mailOptions = {
|
const mailOptions = {
|
||||||
from: '"Maison de Treats" <info@metatron-admin-backend.metatronhost.com>',
|
from: '"Maison de Treats" <info@metatron-admin-backend.metatronhost.com>',
|
||||||
to: toEmail,
|
to: email,
|
||||||
subject: "🎉 New Cake Order from Website Form",
|
subject: "🎉 New Cake Order from Website Form",
|
||||||
html: htmlContent,
|
html: htmlContent,
|
||||||
attachments: [
|
attachments: [
|
||||||
{
|
{ filename: "logo-2.webp", path: "./public/maisondetreats/img/logo-2.webp", cid: "logo" },
|
||||||
filename: "logo-2.webp",
|
{ filename: "thank-you.png", path: "./public/maisondetreats/img/thank-you.png", cid: "banner" },
|
||||||
path: "./public/maisondetreats/img/logo-2.webp",
|
{ filename: "bottom.png", path: "./public/maisondetreats/img/bottom.png", cid: "footer" },
|
||||||
cid: "logo",
|
|
||||||
},
|
|
||||||
{
|
|
||||||
filename: "thank-you.png",
|
|
||||||
path: "./public/maisondetreats/img/thank-you.png",
|
|
||||||
cid: "banner",
|
|
||||||
},
|
|
||||||
{
|
|
||||||
filename: "bottom.png",
|
|
||||||
path: "./public/maisondetreats/img/bottom.png",
|
|
||||||
cid: "footer",
|
|
||||||
},
|
|
||||||
],
|
],
|
||||||
};
|
};
|
||||||
|
|
||||||
await transporter.sendMail(mailOptions);
|
await transporter.sendMail(mailOptions);
|
||||||
console.log("✅ Cake order email sent to", toEmail);
|
console.log("✅ Cake order email sent to", email);
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
console.error("❌ Failed to send cake order email:", err);
|
console.error("❌ Failed to send cake order email:", err);
|
||||||
}
|
}
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user