All files / routes/admin feedback.routes.ts

100% Statements 28/28
100% Branches 18/18
100% Functions 2/2
100% Lines 28/28

Press n or j to go to the next uncovered block, b, p or k for the previous block.

1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105                                            1x     1x 4x 4x 4x 4x 4x         4x     4x       4x 4x                     4x       4x   3x   3x   1x         1x 4x 4x 4x 4x   4x       1x               3x           2x 1x     1x   1x          
// Admin Routes - Feedback Management
 
import { desc, eq } from "drizzle-orm";
import { Hono } from "hono";
import type { Dal } from "../../dal";
import type { getDb } from "../../db";
import { feedback } from "../../db/schema";
import { error, handleError, success } from "../../lib/response";
import { buildPaginationMeta, getPagination } from "../../lib/utils";
import type { Services } from "../../services";
 
type Env = {
	Bindings: CloudflareBindings;
	Variables: {
		user: { id: string; name: string; email: string } | null;
		session: unknown;
		dal: Dal;
		services: Services;
		db: ReturnType<typeof getDb>;
	};
};
 
const feedbackRoutes = new Hono<Env>();
 
// List all feedback with pagination
feedbackRoutes.get("/", async (c) => {
	try {
		const db = c.get("db");
		const page = Number(c.req.query("page") || "1");
		const limit = Number(c.req.query("limit") || "20");
		const status = c.req.query("status");
		const {
			offset,
			limit: safeLimit,
			page: safePage,
		} = getPagination(page, limit);
 
		const statusFilter =
			status && ["new", "reviewed", "resolved"].includes(status)
				? eq(feedback.status, status as "new" | "reviewed" | "resolved")
				: undefined;
 
		const baseQuery = db.select().from(feedback);
		const itemsQuery = statusFilter
			? baseQuery
					.where(statusFilter)
					.orderBy(desc(feedback.createdAt))
					.limit(safeLimit)
					.offset(offset)
			: baseQuery
					.orderBy(desc(feedback.createdAt))
					.limit(safeLimit)
					.offset(offset);
 
		const countQuery = statusFilter
			? db.$count(feedback, statusFilter)
			: db.$count(feedback);
 
		const [items, total] = await Promise.all([itemsQuery, countQuery]);
 
		const meta = buildPaginationMeta(total, safePage, safeLimit);
 
		return success(c, items, 200, meta);
	} catch (err) {
		return handleError(c, err);
	}
});
 
// Update feedback status
feedbackRoutes.patch("/:id", async (c) => {
	try {
		const db = c.get("db");
		const id = Number(c.req.param("id"));
		const body = await c.req.json();
 
		if (
			!body.status ||
			!["new", "reviewed", "resolved"].includes(body.status)
		) {
			return error(
				c,
				"VALIDATION_ERROR",
				"Invalid status. Must be one of: new, reviewed, resolved",
				400,
			);
		}
 
		const [updated] = await db
			.update(feedback)
			.set({ status: body.status })
			.where(eq(feedback.id, id))
			.returning();
 
		if (!updated) {
			return error(c, "NOT_FOUND", "Feedback not found", 404);
		}
 
		return success(c, updated);
	} catch (err) {
		return handleError(c, err);
	}
});
 
export default feedbackRoutes;