seahaven-ap/packages/api/src/routes/departments.ts

81 lines
3 KiB
TypeScript
Raw Normal View History

import { eq } from "drizzle-orm";
import { Hono } from "hono";
import type { Db } from "../db/client.js";
import { departments } from "../db/schema/index.js";
import type { AppBindings } from "../auth/middleware.js";
import { errorJson } from "../http.js";
import { asString, iso, isUuid, parseJsonBody, requireCan } from "./helpers.js";
function toDepartment(row: typeof departments.$inferSelect) {
return {
id: row.id,
code: row.code,
name: row.name,
createdAt: iso(row.createdAt),
updatedAt: iso(row.updatedAt),
};
}
export function createDepartmentRoutes(handle: Db) {
const routes = new Hono<AppBindings>();
routes.get("/departments", async (c) => {
const denied = requireCan(c, "read:invoices");
if (denied) return denied;
const rows = await handle.db.select().from(departments);
return c.json({ items: rows.map(toDepartment) });
});
routes.get("/departments/:id", async (c) => {
const denied = requireCan(c, "read:invoices");
if (denied) return denied;
const id = c.req.param("id");
if (!isUuid(id)) return errorJson(c, 400, "VALIDATION_ERROR", "Invalid department id.");
const row = await handle.db.query.departments.findFirst({ where: eq(departments.id, id) });
if (!row) return errorJson(c, 404, "NOT_FOUND", "Department not found.");
return c.json(toDepartment(row));
});
routes.post("/departments", async (c) => {
const denied = requireCan(c, "admin:settings");
if (denied) return denied;
const body = await parseJsonBody(c);
const code = asString(body.code).trim();
const name = asString(body.name).trim();
if (!code || !name) return errorJson(c, 400, "VALIDATION_ERROR", "Code and name are required.");
const [row] = await handle.db.insert(departments).values({ code, name }).returning();
return c.json(toDepartment(row), 201);
});
routes.patch("/departments/:id", async (c) => {
const denied = requireCan(c, "admin:settings");
if (denied) return denied;
const id = c.req.param("id");
if (!isUuid(id)) return errorJson(c, 400, "VALIDATION_ERROR", "Invalid department id.");
const existing = await handle.db.query.departments.findFirst({
where: eq(departments.id, id),
});
if (!existing) return errorJson(c, 404, "NOT_FOUND", "Department not found.");
const body = await parseJsonBody(c);
const patch: Partial<typeof departments.$inferInsert> = { updatedAt: new Date() };
if (body.code !== undefined) {
const code = asString(body.code).trim();
if (!code) return errorJson(c, 400, "VALIDATION_ERROR", "Code is required.");
patch.code = code;
}
if (body.name !== undefined) {
const name = asString(body.name).trim();
if (!name) return errorJson(c, 400, "VALIDATION_ERROR", "Name is required.");
patch.name = name;
}
const [row] = await handle.db
.update(departments)
.set(patch)
.where(eq(departments.id, id))
.returning();
return c.json(toDepartment(row));
});
return routes;
}