mirror of
https://github.com/Sea-Haven-Industries/shoc-frontend-new.git
synced 2026-09-30 10:23:11 +00:00
* feat(vendors): support company roster workflows * fix(vendors): address roster review feedback * test(vendors): preserve draft on roster load failure * fix(vendors): preserve current roster selection
224 lines
6.8 KiB
JavaScript
224 lines
6.8 KiB
JavaScript
import http from "node:http";
|
|
|
|
const port = Number(process.env.VENDOR_MOCK_PORT ?? 5142);
|
|
let nextCompanyId = 103;
|
|
let nextTechnicianId = 3;
|
|
const companies = [
|
|
{
|
|
companyId: 101,
|
|
rowVersion: "rv-101-1",
|
|
name: "Gateway Plumbing",
|
|
companyPhone: "(314) 555-0100",
|
|
email: "dispatch@gateway.test",
|
|
address: "1 Market St",
|
|
city: "St. Louis",
|
|
state: "MO",
|
|
zip: "63101",
|
|
googleMapsUrl: "https://maps.google.com/gateway",
|
|
notes: "Preferred after-hours vendor",
|
|
technicians: [
|
|
{
|
|
id: 1,
|
|
contactName: "Adam Whyte",
|
|
phone: "(314) 555-0198",
|
|
email: "adam@gateway.test",
|
|
preferredContact: "Email",
|
|
tradeSpecialties: "Plumbing",
|
|
isActive: true,
|
|
totalJobs: 72,
|
|
},
|
|
],
|
|
},
|
|
{
|
|
companyId: 102,
|
|
rowVersion: "rv-102-1",
|
|
name: "Metro HVAC",
|
|
companyPhone: "(314) 555-0110",
|
|
email: "dispatch@metro.test",
|
|
address: "2 Pine St",
|
|
city: "Clayton",
|
|
state: "MO",
|
|
zip: "63105",
|
|
googleMapsUrl: "",
|
|
notes: "",
|
|
technicians: [],
|
|
},
|
|
];
|
|
|
|
function json(response, body, status = 200) {
|
|
response.writeHead(status, {
|
|
"content-type": "application/json",
|
|
"access-control-allow-origin": "*",
|
|
});
|
|
response.end(JSON.stringify(body));
|
|
}
|
|
|
|
async function readBody(request) {
|
|
const chunks = [];
|
|
for await (const chunk of request) chunks.push(chunk);
|
|
return chunks.length ? JSON.parse(Buffer.concat(chunks).toString("utf8")) : {};
|
|
}
|
|
|
|
function findCompany(url) {
|
|
const vendorId = url.searchParams.get("vendorId");
|
|
const companyId = url.searchParams.get("companyId");
|
|
if (vendorId) {
|
|
return companies.find((company) =>
|
|
company.technicians.some((technician) => String(technician.id) === vendorId),
|
|
);
|
|
}
|
|
return companies.find((company) => String(company.companyId) === companyId);
|
|
}
|
|
|
|
function companyFacet(company) {
|
|
return {
|
|
companyId: company.companyId,
|
|
name: company.name,
|
|
companyPhone: company.companyPhone,
|
|
email: company.email,
|
|
address: company.address,
|
|
city: company.city,
|
|
state: company.state,
|
|
zip: company.zip,
|
|
googleMapsUrl: company.googleMapsUrl,
|
|
};
|
|
}
|
|
|
|
function directoryRows() {
|
|
return companies.flatMap((company) =>
|
|
company.technicians.length
|
|
? company.technicians.map((technician) => ({
|
|
Id: technician.id,
|
|
CompanyId: company.companyId,
|
|
CompanyName: company.name,
|
|
ContactName: technician.contactName,
|
|
Email: technician.email,
|
|
Phone: technician.phone,
|
|
CompanyPhone: company.companyPhone,
|
|
City: company.city,
|
|
State: company.state,
|
|
TradeSpecialties: technician.tradeSpecialties,
|
|
GoogleMapsUrl: company.googleMapsUrl,
|
|
Notes: company.notes,
|
|
TotalJobs: technician.totalJobs,
|
|
IsActive: technician.isActive,
|
|
PreferredContact: technician.preferredContact,
|
|
}))
|
|
: [
|
|
{
|
|
Id: null,
|
|
CompanyId: company.companyId,
|
|
CompanyName: company.name,
|
|
ContactName: "",
|
|
Email: company.email,
|
|
Phone: "",
|
|
CompanyPhone: company.companyPhone,
|
|
City: company.city,
|
|
State: company.state,
|
|
TradeSpecialties: "",
|
|
GoogleMapsUrl: company.googleMapsUrl,
|
|
Notes: company.notes,
|
|
TotalJobs: 0,
|
|
IsActive: true,
|
|
PreferredContact: "Phone",
|
|
},
|
|
],
|
|
);
|
|
}
|
|
|
|
const server = http.createServer(async (request, response) => {
|
|
const url = new URL(request.url ?? "/", `http://${request.headers.host}`);
|
|
if (request.method === "OPTIONS") return json(response, {});
|
|
|
|
if (url.pathname === "/api/Vendor/GetVendorList") {
|
|
const search = (url.searchParams.get("search") ?? "").toLowerCase();
|
|
const active = url.searchParams.get("isActive") !== "false";
|
|
const rows = directoryRows().filter(
|
|
(row) =>
|
|
row.IsActive === active &&
|
|
(!search ||
|
|
`${row.CompanyName} ${row.ContactName} ${row.TradeSpecialties} ${row.City} ${row.State}`
|
|
.toLowerCase()
|
|
.includes(search)),
|
|
);
|
|
return json(response, { Data: rows, TotalCount: rows.length, PageNumber: 1, PageSize: 12 });
|
|
}
|
|
|
|
if (url.pathname === "/api/Vendor/facets") {
|
|
return json(response, {
|
|
data: {
|
|
companies: companies.map(companyFacet),
|
|
trades: ["Plumbing", "HVAC"],
|
|
locations: companies.map((company) => ({
|
|
city: company.city,
|
|
state: company.state,
|
|
label: `${company.city}, ${company.state}`,
|
|
})),
|
|
jobBuckets: [],
|
|
},
|
|
});
|
|
}
|
|
|
|
if (url.pathname === "/api/vendor-company-roster" && request.method === "GET") {
|
|
const company = findCompany(url);
|
|
return company ? json(response, company) : json(response, { message: "Not found" }, 404);
|
|
}
|
|
|
|
if (url.pathname === "/api/vendor-company-roster" && request.method === "POST") {
|
|
const body = await readBody(request);
|
|
const company = {
|
|
...body,
|
|
companyId: nextCompanyId++,
|
|
rowVersion: `rv-${Date.now()}`,
|
|
technicians: (body.technicians ?? []).map((technician) => ({
|
|
...technician,
|
|
id: nextTechnicianId++,
|
|
totalJobs: 0,
|
|
})),
|
|
};
|
|
companies.push(company);
|
|
return json(response, company);
|
|
}
|
|
|
|
const rosterMatch = url.pathname.match(/^\/api\/vendor-company-roster\/(\d+)$/);
|
|
if (rosterMatch && request.method === "PUT") {
|
|
const company = companies.find((item) => item.companyId === Number(rosterMatch[1]));
|
|
if (!company) return json(response, { message: "Not found" }, 404);
|
|
const body = await readBody(request);
|
|
Object.assign(company, body, {
|
|
companyId: company.companyId,
|
|
rowVersion: `rv-${Date.now()}`,
|
|
technicians: (body.technicians ?? []).map((technician) => ({
|
|
...technician,
|
|
id: technician.id ?? nextTechnicianId++,
|
|
totalJobs:
|
|
company.technicians.find((existing) => existing.id === technician.id)?.totalJobs ?? 0,
|
|
})),
|
|
});
|
|
return json(response, company);
|
|
}
|
|
|
|
const portalMatch = url.pathname.match(/^\/api\/vendors\/(\d+)\/portal-token$/);
|
|
if (portalMatch) {
|
|
return json(response, {
|
|
data: {
|
|
portalUrl: "https://vendor.example.test/token",
|
|
issuedAt: new Date().toISOString(),
|
|
expiresAt: new Date(Date.now() + 86_400_000).toISOString(),
|
|
},
|
|
});
|
|
}
|
|
|
|
const impactMatch = url.pathname.match(/^\/api\/vendors\/(\d+)\/deactivation-impact$/);
|
|
if (impactMatch) {
|
|
return json(response, {
|
|
data: { vendorId: Number(impactMatch[1]), canDeactivate: true, openWorkOrders: [] },
|
|
});
|
|
}
|
|
|
|
return json(response, { message: `No mock for ${request.method} ${url.pathname}` }, 404);
|
|
});
|
|
|
|
server.listen(port, "127.0.0.1", () => {
|
|
process.stdout.write(`Vendor E2E mock listening on http://127.0.0.1:${port}\n`);
|
|
});
|