Files
mrp/server/src/modules/crm/router.ts
T

159 lines
4.8 KiB
TypeScript
Raw Normal View History

2026-03-14 16:08:29 -05:00
import { crmRecordStatuses, permissions } from "@mrp/shared";
2026-03-14 14:44:40 -05:00
import { Router } from "express";
2026-03-14 16:08:29 -05:00
import { z } from "zod";
2026-03-14 14:44:40 -05:00
2026-03-14 16:08:29 -05:00
import { fail, ok } from "../../lib/http.js";
2026-03-14 14:44:40 -05:00
import { requirePermissions } from "../../lib/rbac.js";
2026-03-14 16:08:29 -05:00
import {
createCustomer,
createVendor,
getCustomerById,
getVendorById,
listCustomers,
listVendors,
updateCustomer,
updateVendor,
} from "./service.js";
const crmRecordSchema = z.object({
name: z.string().trim().min(1),
email: z.string().trim().email(),
phone: z.string().trim().min(1),
addressLine1: z.string().trim().min(1),
addressLine2: z.string(),
city: z.string().trim().min(1),
state: z.string().trim().min(1),
postalCode: z.string().trim().min(1),
country: z.string().trim().min(1),
status: z.enum(crmRecordStatuses),
notes: z.string(),
});
const crmListQuerySchema = z.object({
q: z.string().optional(),
state: z.string().optional(),
status: z.enum(crmRecordStatuses).optional(),
});
function getRouteParam(value: string | string[] | undefined) {
return typeof value === "string" ? value : null;
}
2026-03-14 14:44:40 -05:00
export const crmRouter = Router();
crmRouter.get("/customers", requirePermissions([permissions.crmRead]), async (_request, response) => {
2026-03-14 16:08:29 -05:00
const parsed = crmListQuerySchema.safeParse(_request.query);
if (!parsed.success) {
return fail(response, 400, "INVALID_INPUT", "CRM filters are invalid.");
}
return ok(
response,
await listCustomers({
query: parsed.data.q,
status: parsed.data.status,
state: parsed.data.state,
})
);
});
crmRouter.get("/customers/:customerId", requirePermissions([permissions.crmRead]), async (request, response) => {
const customerId = getRouteParam(request.params.customerId);
if (!customerId) {
return fail(response, 400, "INVALID_INPUT", "Customer id is invalid.");
}
const customer = await getCustomerById(customerId);
if (!customer) {
return fail(response, 404, "CRM_CUSTOMER_NOT_FOUND", "Customer record was not found.");
}
return ok(response, customer);
});
crmRouter.post("/customers", requirePermissions([permissions.crmWrite]), async (request, response) => {
const parsed = crmRecordSchema.safeParse(request.body);
if (!parsed.success) {
return fail(response, 400, "INVALID_INPUT", "Customer payload is invalid.");
}
return ok(response, await createCustomer(parsed.data), 201);
});
crmRouter.put("/customers/:customerId", requirePermissions([permissions.crmWrite]), async (request, response) => {
const customerId = getRouteParam(request.params.customerId);
if (!customerId) {
return fail(response, 400, "INVALID_INPUT", "Customer id is invalid.");
}
const parsed = crmRecordSchema.safeParse(request.body);
if (!parsed.success) {
return fail(response, 400, "INVALID_INPUT", "Customer payload is invalid.");
}
const customer = await updateCustomer(customerId, parsed.data);
if (!customer) {
return fail(response, 404, "CRM_CUSTOMER_NOT_FOUND", "Customer record was not found.");
}
return ok(response, customer);
2026-03-14 14:44:40 -05:00
});
crmRouter.get("/vendors", requirePermissions([permissions.crmRead]), async (_request, response) => {
2026-03-14 16:08:29 -05:00
const parsed = crmListQuerySchema.safeParse(_request.query);
if (!parsed.success) {
return fail(response, 400, "INVALID_INPUT", "CRM filters are invalid.");
}
return ok(
response,
await listVendors({
query: parsed.data.q,
status: parsed.data.status,
state: parsed.data.state,
})
);
});
crmRouter.get("/vendors/:vendorId", requirePermissions([permissions.crmRead]), async (request, response) => {
const vendorId = getRouteParam(request.params.vendorId);
if (!vendorId) {
return fail(response, 400, "INVALID_INPUT", "Vendor id is invalid.");
}
const vendor = await getVendorById(vendorId);
if (!vendor) {
return fail(response, 404, "CRM_VENDOR_NOT_FOUND", "Vendor record was not found.");
}
return ok(response, vendor);
2026-03-14 14:44:40 -05:00
});
2026-03-14 16:08:29 -05:00
crmRouter.post("/vendors", requirePermissions([permissions.crmWrite]), async (request, response) => {
const parsed = crmRecordSchema.safeParse(request.body);
if (!parsed.success) {
return fail(response, 400, "INVALID_INPUT", "Vendor payload is invalid.");
}
return ok(response, await createVendor(parsed.data), 201);
});
crmRouter.put("/vendors/:vendorId", requirePermissions([permissions.crmWrite]), async (request, response) => {
const vendorId = getRouteParam(request.params.vendorId);
if (!vendorId) {
return fail(response, 400, "INVALID_INPUT", "Vendor id is invalid.");
}
const parsed = crmRecordSchema.safeParse(request.body);
if (!parsed.success) {
return fail(response, 400, "INVALID_INPUT", "Vendor payload is invalid.");
}
const vendor = await updateVendor(vendorId, parsed.data);
if (!vendor) {
return fail(response, 404, "CRM_VENDOR_NOT_FOUND", "Vendor record was not found.");
}
return ok(response, vendor);
});