Files
SkyArtShop/backend/routes/admin.js

958 lines
24 KiB
JavaScript
Raw Normal View History

const express = require("express");
const { query } = require("../config/database");
const { requireAuth } = require("../middleware/auth");
2026-01-01 22:24:30 -06:00
const { cache } = require("../middleware/cache");
2026-01-04 17:52:37 -06:00
const { apiLimiter } = require("../config/rateLimiter");
2026-01-01 22:24:30 -06:00
const {
invalidateProductCache,
invalidateBlogCache,
invalidatePortfolioCache,
invalidateHomepageCache,
} = require("../utils/cacheInvalidation");
2025-12-19 20:44:46 -06:00
const logger = require("../config/logger");
const { asyncHandler } = require("../middleware/errorHandler");
2025-12-24 00:13:23 -06:00
const {
sendSuccess,
sendError,
sendNotFound,
} = require("../utils/responseHelpers");
2025-12-19 20:44:46 -06:00
const { getById, deleteById, countRecords } = require("../utils/queryHelpers");
const { HTTP_STATUS } = require("../config/constants");
const router = express.Router();
2026-01-04 17:52:37 -06:00
// Apply rate limiting to all admin routes
router.use(apiLimiter);
// Dashboard stats API
2025-12-24 00:13:23 -06:00
router.get(
"/dashboard/stats",
requireAuth,
asyncHandler(async (req, res) => {
const [productsCount, projectsCount, blogCount, pagesCount] =
await Promise.all([
countRecords("products"),
countRecords("portfolioprojects"),
countRecords("blogposts"),
countRecords("pages"),
]);
sendSuccess(res, {
stats: {
products: productsCount,
projects: projectsCount,
blog: blogCount,
pages: pagesCount,
},
user: {
name: req.session.name,
email: req.session.email,
role: req.session.role,
},
});
})
);
2025-12-19 20:44:46 -06:00
// Generic CRUD factory function
const createCRUDRoutes = (config) => {
const { table, resourceName, listFields = "*", requiresAuth = true } = config;
const auth = requiresAuth ? requireAuth : (req, res, next) => next();
// List all
2025-12-24 00:13:23 -06:00
router.get(
`/${resourceName}`,
auth,
asyncHandler(async (req, res) => {
const result = await query(
`SELECT ${listFields} FROM ${table} ORDER BY createdat DESC`
);
sendSuccess(res, { [resourceName]: result.rows });
})
);
2025-12-19 20:44:46 -06:00
// Get by ID
2025-12-24 00:13:23 -06:00
router.get(
`/${resourceName}/:id`,
auth,
asyncHandler(async (req, res) => {
const item = await getById(table, req.params.id);
if (!item) {
return sendNotFound(res, resourceName);
}
const responseKey = resourceName.slice(0, -1); // Remove 's' for singular
sendSuccess(res, { [responseKey]: item });
})
);
2025-12-19 20:44:46 -06:00
// Delete
2025-12-24 00:13:23 -06:00
router.delete(
`/${resourceName}/:id`,
auth,
asyncHandler(async (req, res) => {
const deleted = await deleteById(table, req.params.id);
if (!deleted) {
return sendNotFound(res, resourceName);
}
sendSuccess(res, { message: `${resourceName} deleted successfully` });
})
);
};
// Helper function to generate slug
const generateSlug = (name) => {
return name
.toLowerCase()
.replace(/[^a-z0-9\s-]/g, "")
.replace(/\s+/g, "-")
.replace(/-+/g, "-")
.trim();
2025-12-19 20:44:46 -06:00
};
// Products CRUD
2025-12-24 00:13:23 -06:00
router.get(
"/products",
requireAuth,
asyncHandler(async (req, res) => {
const result = await query(
`SELECT p.id, p.name, p.price, p.stockquantity, p.isactive, p.isfeatured,
p.isbestseller, p.category, p.createdat,
(SELECT COUNT(*) FROM product_images WHERE product_id = p.id) as image_count
FROM products p
ORDER BY p.createdat DESC`
);
sendSuccess(res, { products: result.rows });
})
);
router.get(
"/products/:id",
requireAuth,
asyncHandler(async (req, res) => {
// Get product details
const product = await getById("products", req.params.id);
if (!product) {
return sendNotFound(res, "Product");
}
2025-12-19 20:44:46 -06:00
2025-12-24 00:13:23 -06:00
// Get associated images with color variants
const imagesResult = await query(
`SELECT id, image_url, color_variant, alt_text, display_order, is_primary
FROM product_images
WHERE product_id = $1
ORDER BY display_order ASC, created_at ASC`,
[req.params.id]
);
2025-12-19 20:44:46 -06:00
2025-12-24 00:13:23 -06:00
product.images = imagesResult.rows;
sendSuccess(res, { product });
})
);
router.post(
"/products",
requireAuth,
asyncHandler(async (req, res) => {
const {
name,
shortdescription,
description,
price,
stockquantity,
category,
sku,
weight,
dimensions,
material,
isactive,
isfeatured,
isbestseller,
images,
} = req.body;
// Generate unique ID and slug from name
const productId =
"prod-" + Date.now() + "-" + Math.random().toString(36).substr(2, 9);
const slug = generateSlug(name);
// Insert product
const productResult = await query(
`INSERT INTO products (
id, name, slug, shortdescription, description, price, stockquantity,
category, sku, weight, dimensions, material, isactive, isfeatured,
isbestseller, createdat
) VALUES ($1, $2, $3, $4, $5, $6, $7, $8, $9, $10, $11, $12, $13, $14, $15, NOW())
RETURNING *`,
[
productId,
name,
slug,
shortdescription,
description,
price,
stockquantity || 0,
category,
sku,
weight,
dimensions,
material,
isactive !== false,
isfeatured || false,
isbestseller || false,
]
);
2025-12-19 20:44:46 -06:00
2025-12-24 00:13:23 -06:00
const product = productResult.rows[0];
// Insert images with color variants if provided
if (images && Array.isArray(images) && images.length > 0) {
for (let i = 0; i < images.length; i++) {
const img = images[i];
await query(
`INSERT INTO product_images (
product_id, image_url, color_variant, color_code, alt_text, display_order, is_primary, variant_price, variant_stock
) VALUES ($1, $2, $3, $4, $5, $6, $7, $8, $9)`,
[
product.id,
img.image_url,
img.color_variant || null,
img.color_code || null,
img.alt_text || name,
img.display_order || i,
img.is_primary || i === 0,
img.variant_price || null,
img.variant_stock || 0,
]
);
}
}
// Fetch complete product with images
const completeProduct = await query(
`SELECT p.*,
json_agg(
json_build_object(
'id', pi.id,
'image_url', pi.image_url,
'color_variant', pi.color_variant,
'color_code', pi.color_code,
'alt_text', pi.alt_text,
'display_order', pi.display_order,
'is_primary', pi.is_primary,
'variant_price', pi.variant_price,
'variant_stock', pi.variant_stock
) ORDER BY pi.display_order
) FILTER (WHERE pi.id IS NOT NULL) as images
FROM products p
LEFT JOIN product_images pi ON pi.product_id = p.id
WHERE p.id = $1
GROUP BY p.id`,
[product.id]
);
sendSuccess(
res,
{
product: completeProduct.rows[0],
message: "Product created successfully",
},
HTTP_STATUS.CREATED
);
})
);
router.put(
"/products/:id",
requireAuth,
asyncHandler(async (req, res) => {
2026-01-01 22:24:30 -06:00
console.log("=== UPDATE PRODUCT API CALLED ===");
console.log("Product ID:", req.params.id);
console.log("Request body:", JSON.stringify(req.body, null, 2));
2025-12-24 00:13:23 -06:00
const {
name,
shortdescription,
description,
price,
stockquantity,
category,
sku,
weight,
dimensions,
material,
isactive,
isfeatured,
isbestseller,
images,
} = req.body;
2026-01-01 22:24:30 -06:00
console.log("Images to save:", images);
2025-12-24 00:13:23 -06:00
// Generate slug if name is provided
const slug = name ? generateSlug(name) : null;
// Build dynamic update query
const updates = [];
const values = [];
let paramIndex = 1;
if (name !== undefined) {
updates.push(`name = $${paramIndex++}`);
values.push(name);
updates.push(`slug = $${paramIndex++}`);
values.push(slug);
}
if (shortdescription !== undefined) {
updates.push(`shortdescription = $${paramIndex++}`);
values.push(shortdescription);
}
if (description !== undefined) {
updates.push(`description = $${paramIndex++}`);
values.push(description);
}
if (price !== undefined) {
updates.push(`price = $${paramIndex++}`);
values.push(price);
}
if (stockquantity !== undefined) {
updates.push(`stockquantity = $${paramIndex++}`);
values.push(stockquantity);
}
if (category !== undefined) {
updates.push(`category = $${paramIndex++}`);
values.push(category);
}
if (sku !== undefined) {
updates.push(`sku = $${paramIndex++}`);
values.push(sku);
}
if (weight !== undefined) {
updates.push(`weight = $${paramIndex++}`);
values.push(weight);
}
if (dimensions !== undefined) {
updates.push(`dimensions = $${paramIndex++}`);
values.push(dimensions);
}
if (material !== undefined) {
updates.push(`material = $${paramIndex++}`);
values.push(material);
}
if (isactive !== undefined) {
updates.push(`isactive = $${paramIndex++}`);
values.push(isactive);
}
if (isfeatured !== undefined) {
updates.push(`isfeatured = $${paramIndex++}`);
values.push(isfeatured);
}
if (isbestseller !== undefined) {
updates.push(`isbestseller = $${paramIndex++}`);
values.push(isbestseller);
}
updates.push(`updatedat = NOW()`);
values.push(req.params.id);
2025-12-19 20:44:46 -06:00
2025-12-24 00:13:23 -06:00
const updateQuery = `UPDATE products SET ${updates.join(
", "
)} WHERE id = $${paramIndex} RETURNING *`;
const result = await query(updateQuery, values);
2025-12-14 01:54:40 -06:00
2025-12-24 00:13:23 -06:00
if (result.rows.length === 0) {
return sendNotFound(res, "Product");
}
2026-01-01 22:24:30 -06:00
console.log("Product updated in database:", result.rows[0].id);
2025-12-24 00:13:23 -06:00
// Update images if provided
if (images && Array.isArray(images)) {
2026-01-01 22:24:30 -06:00
console.log("Updating images, count:", images.length);
2025-12-24 00:13:23 -06:00
// Delete existing images for this product
2026-01-01 22:24:30 -06:00
const deleteResult = await query(
"DELETE FROM product_images WHERE product_id = $1",
[req.params.id]
);
console.log("Deleted existing images, count:", deleteResult.rowCount);
2025-12-24 00:13:23 -06:00
// Insert new images
for (let i = 0; i < images.length; i++) {
const img = images[i];
2026-01-01 22:24:30 -06:00
console.log(
`Inserting image ${i + 1}/${images.length}:`,
img.image_url
);
2025-12-24 00:13:23 -06:00
await query(
`INSERT INTO product_images (
product_id, image_url, color_variant, color_code, alt_text, display_order, is_primary, variant_price, variant_stock
) VALUES ($1, $2, $3, $4, $5, $6, $7, $8, $9)`,
[
req.params.id,
img.image_url,
img.color_variant || null,
img.color_code || null,
img.alt_text || result.rows[0].name,
img.display_order || i,
img.is_primary || i === 0,
img.variant_price || null,
img.variant_stock || 0,
]
);
}
2026-01-01 22:24:30 -06:00
console.log("All images inserted successfully");
} else {
console.log("No images to update");
2025-12-24 00:13:23 -06:00
}
// Fetch complete product with images
const completeProduct = await query(
`SELECT p.*,
json_agg(
json_build_object(
'id', pi.id,
'image_url', pi.image_url,
'color_variant', pi.color_variant,
'color_code', pi.color_code,
'alt_text', pi.alt_text,
'display_order', pi.display_order,
'is_primary', pi.is_primary,
'variant_price', pi.variant_price,
'variant_stock', pi.variant_stock
) ORDER BY pi.display_order
) FILTER (WHERE pi.id IS NOT NULL) as images
FROM products p
LEFT JOIN product_images pi ON pi.product_id = p.id
WHERE p.id = $1
GROUP BY p.id`,
[req.params.id]
);
2025-12-14 01:54:40 -06:00
2026-01-01 22:24:30 -06:00
console.log("Final product with images:", completeProduct.rows[0]);
console.log("=== PRODUCT UPDATE COMPLETE ===");
// Invalidate product cache
invalidateProductCache();
2025-12-24 00:13:23 -06:00
sendSuccess(res, {
product: completeProduct.rows[0],
message: "Product updated successfully",
});
})
);
router.delete(
"/products/:id",
requireAuth,
asyncHandler(async (req, res) => {
// Product images will be deleted automatically via CASCADE
const deleted = await deleteById("products", req.params.id);
if (!deleted) {
return sendNotFound(res, "Product");
}
sendSuccess(res, { message: "Product deleted successfully" });
})
);
2025-12-19 20:44:46 -06:00
// Portfolio Projects CRUD
2025-12-24 00:13:23 -06:00
router.get(
"/portfolio/projects",
requireAuth,
asyncHandler(async (req, res) => {
const result = await query(
"SELECT id, title, description, imageurl, category, isactive, createdat FROM portfolioprojects ORDER BY createdat DESC"
);
sendSuccess(res, { projects: result.rows });
})
);
router.get(
"/portfolio/projects/:id",
requireAuth,
asyncHandler(async (req, res) => {
const project = await getById("portfolioprojects", req.params.id);
if (!project) {
return sendNotFound(res, "Project");
}
sendSuccess(res, { project });
})
);
router.post(
"/portfolio/projects",
requireAuth,
asyncHandler(async (req, res) => {
const { title, description, category, isactive, imageurl } = req.body;
const result = await query(
`INSERT INTO portfolioprojects (title, description, category, isactive, imageurl, createdat)
VALUES ($1, $2, $3, $4, $5, NOW()) RETURNING *`,
[title, description, category, isactive !== false, imageurl || null]
);
sendSuccess(
res,
{
project: result.rows[0],
message: "Project created successfully",
},
HTTP_STATUS.CREATED
);
})
);
router.put(
"/portfolio/projects/:id",
requireAuth,
asyncHandler(async (req, res) => {
const { title, description, category, isactive, imageurl } = req.body;
const result = await query(
`UPDATE portfolioprojects
SET title = $1, description = $2, category = $3, isactive = $4, imageurl = $5, updatedat = NOW()
WHERE id = $6 RETURNING *`,
[
title,
description,
category,
isactive !== false,
imageurl || null,
req.params.id,
]
);
if (result.rows.length === 0) {
return sendNotFound(res, "Project");
}
sendSuccess(res, {
project: result.rows[0],
message: "Project updated successfully",
});
})
);
router.delete(
"/portfolio/projects/:id",
requireAuth,
asyncHandler(async (req, res) => {
const deleted = await deleteById("portfolioprojects", req.params.id);
if (!deleted) {
return sendNotFound(res, "Project");
}
sendSuccess(res, { message: "Project deleted successfully" });
})
);
2025-12-19 20:44:46 -06:00
// Blog Posts CRUD
2025-12-24 00:13:23 -06:00
router.get(
"/blog",
requireAuth,
asyncHandler(async (req, res) => {
const result = await query(
"SELECT id, title, slug, excerpt, ispublished, createdat FROM blogposts ORDER BY createdat DESC"
);
sendSuccess(res, { posts: result.rows });
})
);
router.get(
"/blog/:id",
requireAuth,
asyncHandler(async (req, res) => {
const post = await getById("blogposts", req.params.id);
if (!post) {
return sendNotFound(res, "Blog post");
}
sendSuccess(res, { post });
})
);
router.post(
"/blog",
requireAuth,
asyncHandler(async (req, res) => {
const {
title,
slug,
excerpt,
content,
metatitle,
metadescription,
ispublished,
} = req.body;
const result = await query(
`INSERT INTO blogposts (title, slug, excerpt, content, metatitle, metadescription, ispublished, createdat)
2025-12-19 20:44:46 -06:00
VALUES ($1, $2, $3, $4, $5, $6, $7, NOW()) RETURNING *`,
2025-12-24 00:13:23 -06:00
[
title,
slug,
excerpt,
content,
metatitle,
metadescription,
ispublished || false,
]
);
sendSuccess(
res,
{
post: result.rows[0],
message: "Blog post created successfully",
},
HTTP_STATUS.CREATED
);
})
);
router.put(
"/blog/:id",
requireAuth,
asyncHandler(async (req, res) => {
const {
title,
slug,
excerpt,
content,
metatitle,
metadescription,
ispublished,
} = req.body;
const result = await query(
`UPDATE blogposts
2025-12-19 20:44:46 -06:00
SET title = $1, slug = $2, excerpt = $3, content = $4, metatitle = $5,
metadescription = $6, ispublished = $7, updatedat = NOW()
WHERE id = $8 RETURNING *`,
2025-12-24 00:13:23 -06:00
[
title,
slug,
excerpt,
content,
metatitle,
metadescription,
ispublished || false,
req.params.id,
]
);
if (result.rows.length === 0) {
return sendNotFound(res, "Blog post");
}
sendSuccess(res, {
post: result.rows[0],
message: "Blog post updated successfully",
});
})
);
router.delete(
"/blog/:id",
requireAuth,
asyncHandler(async (req, res) => {
const deleted = await deleteById("blogposts", req.params.id);
if (!deleted) {
return sendNotFound(res, "Blog post");
}
sendSuccess(res, { message: "Blog post deleted successfully" });
})
);
2025-12-14 01:54:40 -06:00
// Custom Pages CRUD
2025-12-24 00:13:23 -06:00
router.get(
"/pages",
requireAuth,
asyncHandler(async (req, res) => {
const result = await query(
"SELECT id, title, slug, ispublished, createdat FROM pages ORDER BY createdat DESC"
);
sendSuccess(res, { pages: result.rows });
})
);
router.get(
"/pages/:id",
requireAuth,
asyncHandler(async (req, res) => {
const page = await getById("pages", req.params.id);
if (!page) {
return sendNotFound(res, "Page");
}
sendSuccess(res, { page });
})
);
router.post(
"/pages",
requireAuth,
asyncHandler(async (req, res) => {
const {
title,
slug,
content,
contenthtml,
metatitle,
metadescription,
ispublished,
pagedata,
} = req.body;
2026-01-01 22:24:30 -06:00
// Generate readable ID from slug
const pageId = `page-${slug}`;
2025-12-24 00:13:23 -06:00
const result = await query(
2026-01-01 22:24:30 -06:00
`INSERT INTO pages (id, title, slug, content, pagecontent, metatitle, metadescription, ispublished, isactive, pagedata, createdat)
VALUES ($1, $2, $3, $4, $5, $6, $7, $8, $9, $10, NOW()) RETURNING *`,
2025-12-24 00:13:23 -06:00
[
2026-01-01 22:24:30 -06:00
pageId,
2025-12-24 00:13:23 -06:00
title,
slug,
content,
contenthtml || content,
metatitle,
metadescription,
ispublished !== false,
ispublished !== false,
pagedata ? JSON.stringify(pagedata) : null,
]
);
sendSuccess(
res,
{
page: result.rows[0],
message: "Page created successfully",
},
HTTP_STATUS.CREATED
);
})
);
router.put(
"/pages/:id",
requireAuth,
asyncHandler(async (req, res) => {
const {
title,
slug,
content,
contenthtml,
metatitle,
metadescription,
ispublished,
pagedata,
} = req.body;
const result = await query(
`UPDATE pages
SET title = $1, slug = $2, content = $3, pagecontent = $4, metatitle = $5,
metadescription = $6, ispublished = $7, isactive = $8, pagedata = $9, updatedat = NOW()
WHERE id = $10 RETURNING *`,
[
title,
slug,
content,
contenthtml || content,
metatitle,
metadescription,
ispublished !== false,
ispublished !== false,
pagedata ? JSON.stringify(pagedata) : null,
req.params.id,
]
);
if (result.rows.length === 0) {
return sendNotFound(res, "Page");
}
sendSuccess(res, {
page: result.rows[0],
message: "Page updated successfully",
});
})
);
router.delete(
"/pages/:id",
requireAuth,
asyncHandler(async (req, res) => {
const deleted = await deleteById("pages", req.params.id);
if (!deleted) {
return sendNotFound(res, "Page");
}
sendSuccess(res, { message: "Page deleted successfully" });
})
);
2025-12-14 01:54:40 -06:00
2025-12-19 20:44:46 -06:00
// Settings Management
const settingsHandler = (key) => ({
get: asyncHandler(async (req, res) => {
2025-12-14 01:54:40 -06:00
const result = await query(
2025-12-19 20:44:46 -06:00
"SELECT settings FROM site_settings WHERE key = $1",
[key]
2025-12-14 01:54:40 -06:00
);
const settings = result.rows.length > 0 ? result.rows[0].settings : {};
2025-12-19 20:44:46 -06:00
sendSuccess(res, { settings });
}),
post: asyncHandler(async (req, res) => {
2025-12-14 01:54:40 -06:00
const settings = req.body;
await query(
`INSERT INTO site_settings (key, settings, updatedat)
2025-12-19 20:44:46 -06:00
VALUES ($1, $2, NOW())
ON CONFLICT (key) DO UPDATE SET settings = $2, updatedat = NOW()`,
[key, JSON.stringify(settings)]
2025-12-14 01:54:40 -06:00
);
2025-12-19 20:44:46 -06:00
sendSuccess(res, { message: `${key} settings saved successfully` });
}),
2025-12-14 01:54:40 -06:00
});
2025-12-19 20:44:46 -06:00
// Homepage Settings
const homepageSettings = settingsHandler("homepage");
router.get("/homepage/settings", requireAuth, homepageSettings.get);
router.post("/homepage/settings", requireAuth, homepageSettings.post);
2025-12-14 01:54:40 -06:00
2025-12-19 20:44:46 -06:00
// General Settings
const generalSettings = settingsHandler("general");
router.get("/settings", requireAuth, generalSettings.get);
router.post("/settings", requireAuth, generalSettings.post);
2025-12-14 01:54:40 -06:00
// Menu Management
2025-12-24 00:13:23 -06:00
router.get(
"/menu",
requireAuth,
asyncHandler(async (req, res) => {
const result = await query(
"SELECT settings FROM site_settings WHERE key = 'menu'"
);
const items =
result.rows.length > 0 ? result.rows[0].settings.items || [] : [];
sendSuccess(res, { items });
})
);
router.post(
"/menu",
requireAuth,
asyncHandler(async (req, res) => {
const { items } = req.body;
await query(
`INSERT INTO site_settings (key, settings, updatedat)
2025-12-19 20:44:46 -06:00
VALUES ('menu', $1, NOW())
ON CONFLICT (key) DO UPDATE SET settings = $1, updatedat = NOW()`,
2025-12-24 00:13:23 -06:00
[JSON.stringify({ items })]
);
sendSuccess(res, { message: "Menu saved successfully" });
})
);
// ==================== TEAM MEMBERS CRUD ====================
// Get all team members
router.get(
"/team-members",
requireAuth,
asyncHandler(async (req, res) => {
const result = await query(
"SELECT * FROM team_members ORDER BY display_order ASC, created_at DESC"
);
sendSuccess(res, { teamMembers: result.rows });
})
);
// Get single team member
router.get(
"/team-members/:id",
requireAuth,
asyncHandler(async (req, res) => {
const result = await query("SELECT * FROM team_members WHERE id = $1", [
req.params.id,
]);
if (result.rows.length === 0) {
return sendNotFound(res, "Team member");
}
sendSuccess(res, { teamMember: result.rows[0] });
})
);
// Create team member
router.post(
"/team-members",
requireAuth,
asyncHandler(async (req, res) => {
const { name, position, bio, image_url, display_order } = req.body;
if (!name || !position) {
return sendError(
res,
"Name and position are required",
HTTP_STATUS.BAD_REQUEST
);
}
const result = await query(
`INSERT INTO team_members (name, position, bio, image_url, display_order, updated_at)
VALUES ($1, $2, $3, $4, $5, CURRENT_TIMESTAMP)
RETURNING *`,
[name, position, bio || null, image_url || null, display_order || 0]
);
sendSuccess(
res,
{
teamMember: result.rows[0],
message: "Team member created successfully",
},
HTTP_STATUS.CREATED
);
})
);
// Update team member
router.put(
"/team-members/:id",
requireAuth,
asyncHandler(async (req, res) => {
const { name, position, bio, image_url, display_order } = req.body;
if (!name || !position) {
return sendError(
res,
"Name and position are required",
HTTP_STATUS.BAD_REQUEST
);
}
const result = await query(
`UPDATE team_members
SET name = $1, position = $2, bio = $3, image_url = $4, display_order = $5, updated_at = CURRENT_TIMESTAMP
WHERE id = $6
RETURNING *`,
[name, position, bio, image_url, display_order || 0, req.params.id]
);
if (result.rows.length === 0) {
return sendNotFound(res, "Team member");
}
sendSuccess(res, {
teamMember: result.rows[0],
message: "Team member updated successfully",
});
})
);
// Delete team member
router.delete(
"/team-members/:id",
requireAuth,
asyncHandler(async (req, res) => {
const result = await query(
"DELETE FROM team_members WHERE id = $1 RETURNING *",
[req.params.id]
);
if (result.rows.length === 0) {
return sendNotFound(res, "Team member");
}
sendSuccess(res, { message: "Team member deleted successfully" });
})
);
2025-12-14 01:54:40 -06:00
module.exports = router;