feat(pos): add GET/PUT /api/v1/pos/slip-layout/[letterheadId]
This commit is contained in:
parent
3a29ad6a6a
commit
102673d00b
2 changed files with 128 additions and 0 deletions
72
src/app/api/v1/pos/slip-layout/[letterheadId]/route.test.ts
Normal file
72
src/app/api/v1/pos/slip-layout/[letterheadId]/route.test.ts
Normal file
|
|
@ -0,0 +1,72 @@
|
|||
import { describe, it, expect, vi, beforeEach } from "vitest";
|
||||
import { NextRequest } from "next/server";
|
||||
import { GET, PUT } from "./route";
|
||||
import { getSlipLayout, saveSlipLayout } from "@/lib/server/pos-slip-layout-service";
|
||||
|
||||
vi.mock("@/lib/server/pos-slip-layout-service", () => ({
|
||||
getSlipLayout: vi.fn(),
|
||||
saveSlipLayout: vi.fn(),
|
||||
}));
|
||||
|
||||
const POS_API_KEY = "test-pos-key";
|
||||
|
||||
function req(method: string, body?: unknown, authHeader: string | null = `Bearer ${POS_API_KEY}`): NextRequest {
|
||||
const headers: Record<string, string> = { "content-type": "application/json" };
|
||||
if (authHeader !== null) headers.authorization = authHeader;
|
||||
return new NextRequest("http://localhost/api/v1/pos/slip-layout/lh-1", {
|
||||
method,
|
||||
headers,
|
||||
body: body !== undefined ? JSON.stringify(body) : undefined,
|
||||
});
|
||||
}
|
||||
|
||||
const params = Promise.resolve({ letterheadId: "lh-1" });
|
||||
|
||||
describe("GET /api/v1/pos/slip-layout/[letterheadId]", () => {
|
||||
beforeEach(() => {
|
||||
process.env.POS_API_KEY = POS_API_KEY;
|
||||
vi.mocked(getSlipLayout).mockReset();
|
||||
vi.mocked(saveSlipLayout).mockReset();
|
||||
});
|
||||
|
||||
it("401s without a valid key", async () => {
|
||||
const res = await GET(req("GET", undefined, null), { params });
|
||||
expect(res.status).toBe(401);
|
||||
});
|
||||
|
||||
it("returns { layout: null } when nothing is saved yet", async () => {
|
||||
vi.mocked(getSlipLayout).mockResolvedValue(null);
|
||||
const res = await GET(req("GET"), { params });
|
||||
expect(res.status).toBe(200);
|
||||
const body = await res.json();
|
||||
expect(body.layout).toBeNull();
|
||||
});
|
||||
|
||||
it("returns the saved layout", async () => {
|
||||
vi.mocked(getSlipLayout).mockResolvedValue({
|
||||
sale_blocks: [], delivery_blocks: [], cashup_blocks: [], delivery_show_pricing: true,
|
||||
});
|
||||
const res = await GET(req("GET"), { params });
|
||||
const body = await res.json();
|
||||
expect(body.layout.delivery_show_pricing).toBe(true);
|
||||
});
|
||||
});
|
||||
|
||||
describe("PUT /api/v1/pos/slip-layout/[letterheadId]", () => {
|
||||
beforeEach(() => {
|
||||
process.env.POS_API_KEY = POS_API_KEY;
|
||||
vi.mocked(saveSlipLayout).mockReset();
|
||||
});
|
||||
|
||||
it("saves and returns the updated layout", async () => {
|
||||
vi.mocked(saveSlipLayout).mockResolvedValue({
|
||||
sale_blocks: [{ id: "logo", enabled: false }],
|
||||
delivery_blocks: [], cashup_blocks: [], delivery_show_pricing: true,
|
||||
});
|
||||
const res = await PUT(req("PUT", { sale_blocks: [{ id: "logo", enabled: false }] }), { params });
|
||||
expect(res.status).toBe(200);
|
||||
expect(saveSlipLayout).toHaveBeenCalledWith("lh-1", { sale_blocks: [{ id: "logo", enabled: false }] });
|
||||
const body = await res.json();
|
||||
expect(body.layout.sale_blocks).toEqual([{ id: "logo", enabled: false }]);
|
||||
});
|
||||
});
|
||||
56
src/app/api/v1/pos/slip-layout/[letterheadId]/route.ts
Normal file
56
src/app/api/v1/pos/slip-layout/[letterheadId]/route.ts
Normal file
|
|
@ -0,0 +1,56 @@
|
|||
import { NextRequest, NextResponse } from "next/server";
|
||||
import { assertPosAuth } from "@/lib/server/pos-auth";
|
||||
import {
|
||||
getSlipLayout,
|
||||
saveSlipLayout,
|
||||
type PosSlipLayoutInput,
|
||||
} from "@/lib/server/pos-slip-layout-service";
|
||||
|
||||
/**
|
||||
* GET /api/v1/pos/slip-layout/[letterheadId]
|
||||
*
|
||||
* Returns `{ layout: null }` when this letterhead has never had a
|
||||
* layout saved — the till falls back to its own hardcoded defaults
|
||||
* in that case (see public/pos/slip-layout.js).
|
||||
*
|
||||
* Auth: requires `Authorization: Bearer ${POS_API_KEY}`.
|
||||
*/
|
||||
export async function GET(
|
||||
req: NextRequest,
|
||||
{ params }: { params: Promise<{ letterheadId: string }> },
|
||||
) {
|
||||
try {
|
||||
assertPosAuth(req);
|
||||
} catch (err) {
|
||||
const message = err instanceof Error ? err.message : "Unauthorized";
|
||||
return NextResponse.json({ error: message }, { status: 401 });
|
||||
}
|
||||
const { letterheadId } = await params;
|
||||
const layout = await getSlipLayout(letterheadId);
|
||||
return NextResponse.json({ layout });
|
||||
}
|
||||
|
||||
/**
|
||||
* PUT /api/v1/pos/slip-layout/[letterheadId]
|
||||
*
|
||||
* Body: any subset of `{ sale_blocks, delivery_blocks, cashup_blocks,
|
||||
* delivery_show_pricing }` — only the fields given are written.
|
||||
* Creates the row on first save.
|
||||
*
|
||||
* Auth: requires `Authorization: Bearer ${POS_API_KEY}`.
|
||||
*/
|
||||
export async function PUT(
|
||||
req: NextRequest,
|
||||
{ params }: { params: Promise<{ letterheadId: string }> },
|
||||
) {
|
||||
try {
|
||||
assertPosAuth(req);
|
||||
} catch (err) {
|
||||
const message = err instanceof Error ? err.message : "Unauthorized";
|
||||
return NextResponse.json({ error: message }, { status: 401 });
|
||||
}
|
||||
const { letterheadId } = await params;
|
||||
const body = (await req.json()) as PosSlipLayoutInput;
|
||||
const layout = await saveSlipLayout(letterheadId, body);
|
||||
return NextResponse.json({ layout });
|
||||
}
|
||||
Loading…
Reference in a new issue