const express = require('express'); const router = express.Router(); const nodemailer = require('nodemailer'); const config = require('../config'); // Helper function to create email transporter const createTransporter = () => { return nodemailer.createTransport({ host: config.email.host, port: config.email.port, auth: { user: config.email.user, pass: config.email.pass } }); }; module.exports = (pool, query, authMiddleware) => { // Apply authentication middleware to all routes router.use(authMiddleware); // Get all orders (admin only) router.get('/', async (req, res, next) => { try { // Check if user is admin if (!req.user.is_admin) { return res.status(403).json({ error: true, message: 'Admin access required' }); } const result = await query(` SELECT o.*, u.email, u.first_name, u.last_name, COUNT(oi.id) AS item_count FROM orders o JOIN users u ON o.user_id = u.id LEFT JOIN order_items oi ON o.id = oi.order_id GROUP BY o.id, u.id ORDER BY o.created_at DESC `); res.json(result.rows); } catch (error) { next(error); } }); // Get single order with items router.get('/:id', async (req, res, next) => { try { const { id } = req.params; // Check if user is admin if (!req.user.is_admin) { return res.status(403).json({ error: true, message: 'Admin access required' }); } // Get order details with shipping information const orderResult = await query(` SELECT o.*, u.email, u.first_name, u.last_name, (SELECT EXISTS( SELECT 1 FROM notification_logs WHERE order_id = o.id AND notification_type = 'shipping_notification' )) as notification_sent FROM orders o JOIN users u ON o.user_id = u.id WHERE o.id = $1 `, [id]); if (orderResult.rows.length === 0) { return res.status(404).json({ error: true, message: 'Order not found' }); } // Get order items with product details const itemsResult = await query(` SELECT oi.*, p.name as product_name, p.description as product_description, pc.name as product_category, ( SELECT json_agg( json_build_object( 'id', pi.id, 'path', pi.image_path, 'isPrimary', pi.is_primary, 'displayOrder', pi.display_order ) ORDER BY pi.display_order ) FROM product_images pi WHERE pi.product_id = p.id ) AS product_images FROM order_items oi JOIN products p ON oi.product_id = p.id JOIN product_categories pc ON p.category_id = pc.id WHERE oi.order_id = $1 `, [id]); // Combine order with items const order = { ...orderResult.rows[0], items: itemsResult.rows }; res.json(order); } catch (error) { next(error); } }); // Update order status (simple version) router.patch('/:id', async (req, res, next) => { try { const { id } = req.params; const { status } = req.body; // Check if user is admin if (!req.user.is_admin) { return res.status(403).json({ error: true, message: 'Admin access required' }); } // Validate status const validStatuses = ['pending', 'processing', 'shipped', 'delivered', 'cancelled', 'refunded']; if (!validStatuses.includes(status)) { return res.status(400).json({ error: true, message: `Invalid status. Must be one of: ${validStatuses.join(', ')}` }); } // Update order status const result = await query(` UPDATE orders SET status = $1, updated_at = NOW() WHERE id = $2 RETURNING * `, [status, id]); if (result.rows.length === 0) { return res.status(404).json({ error: true, message: 'Order not found' }); } res.json({ message: 'Order status updated successfully', order: result.rows[0] }); } catch (error) { next(error); } }); // Update order status with shipping information and send notification router.patch('/:id/shipping', async (req, res, next) => { try { const { id } = req.params; const { status, shippingData, sendNotification } = req.body; // Check if user is admin if (!req.user.is_admin) { return res.status(403).json({ error: true, message: 'Admin access required' }); } // Validate status const validStatuses = ['pending', 'processing', 'shipped', 'delivered', 'cancelled', 'refunded']; if (!validStatuses.includes(status)) { return res.status(400).json({ error: true, message: `Invalid status. Must be one of: ${validStatuses.join(', ')}` }); } // Get order with customer information before updating const orderResult = await query(` SELECT o.*, u.email, u.first_name, u.last_name FROM orders o JOIN users u ON o.user_id = u.id WHERE o.id = $1 `, [id]); if (orderResult.rows.length === 0) { return res.status(404).json({ error: true, message: 'Order not found' }); } const order = orderResult.rows[0]; // Begin transaction const client = await pool.connect(); try { await client.query('BEGIN'); // Store shipping information as JSON in the database const updatedOrder = await client.query(` UPDATE orders SET status = $1, updated_at = NOW(), shipping_info = $2, shipping_date = $3 WHERE id = $4 RETURNING * `, [ status, JSON.stringify(shippingData), new Date(), id ]); // If status is 'shipped' and notification requested, send email if (status === 'shipped' && sendNotification) { // Get order items for the email const itemsResult = await client.query(` SELECT oi.*, p.name as product_name, p.price as original_price FROM order_items oi JOIN products p ON oi.product_id = p.id WHERE oi.order_id = $1 `, [id]); const orderItems = itemsResult.rows; // Send email notification await sendShippingNotification( order, orderItems, shippingData ); // Log the notification in the database await client.query(` INSERT INTO notification_logs (order_id, notification_type, sent_at) VALUES ($1, $2, NOW()) `, [id, 'shipping_notification']); } await client.query('COMMIT'); res.json({ message: 'Order status updated successfully', order: updatedOrder.rows[0] }); } catch (error) { await client.query('ROLLBACK'); throw error; } finally { client.release(); } } catch (error) { console.error('Error updating order status with shipping:', error); next(error); } }); // Helper function to send shipping notification email async function sendShippingNotification(order, orderItems, shippingData) { try { const transporter = createTransporter(); // Calculate order total const orderTotal = orderItems.reduce((sum, item) => { return sum + (parseFloat(item.price_at_purchase) * item.quantity); }, 0); // Format shipping date const shippedDate = new Date(shippingData.shippedDate || new Date()).toLocaleDateString(); // Generate items HTML table const itemsHtml = orderItems.map(item => `
Order #${order.id.substring(0, 8)}
Hello ${order.first_name},
Good news! Your order has been shipped and is on its way to you.
${shippingData.customerMessage ? `Message from our team: ${shippingData.customerMessage}
` : ''}Carrier: ${shippingData.shipper || 'Standard Shipping'}
Tracking Number: ${shippingData.trackingNumber}
Shipped On: ${shippedDate}
${shippingData.estimatedDelivery ? `Estimated Delivery: ${shippingData.estimatedDelivery}
` : ''}| Item | Qty | Price | Total |
|---|---|---|---|
| Total: | $${orderTotal.toFixed(2)} | ||
Thank you for your purchase! If you have any questions, please contact our customer service.
© ${new Date().getFullYear()} Rocks, Bones & Sticks. All rights reserved.