feat: polish admin UI, phone login, and product details
Switch admin auth to mobile, restyle shell/login with Vuesax icons, and add product detail fields plus public product pages. Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
@@ -15,12 +15,12 @@ import { PrismaService } from "../src/prisma/prisma.service.js";
|
||||
describe("API (e2e)", () => {
|
||||
let app: INestApplication;
|
||||
let prisma: PrismaService;
|
||||
const suffix = randomBytes(4).toString("hex");
|
||||
const admin = { email: `e2e-admin-${suffix}@test.local`, password: "e2e-Password-1" };
|
||||
const editor = { email: `e2e-editor-${suffix}@test.local`, password: "e2e-Password-2" };
|
||||
const suffix = String(randomBytes(4).readUInt32BE(0) % 10_000_000).padStart(7, "0");
|
||||
const admin = { phone: `0912${suffix}`, password: "e2e-Password-1" };
|
||||
const editor = { phone: `0935${suffix}`, password: "e2e-Password-2" };
|
||||
|
||||
const http = () => request(app.getHttpServer());
|
||||
const login = async (u: { email: string; password: string }) => {
|
||||
const login = async (u: { phone: string; password: string }) => {
|
||||
const res = await http().post("/api/admin/auth/login").send(u).expect(200);
|
||||
return { token: res.body.accessToken as string, cookie: res.headers["set-cookie"] as unknown as string[] };
|
||||
};
|
||||
@@ -35,14 +35,14 @@ describe("API (e2e)", () => {
|
||||
prisma = app.get(PrismaService);
|
||||
await prisma.adminUser.createMany({
|
||||
data: [
|
||||
{ email: admin.email, name: "e2e admin", role: "ADMIN", passwordHash: await hashPassword(admin.password) },
|
||||
{ email: editor.email, name: "e2e editor", role: "EDITOR", passwordHash: await hashPassword(editor.password) },
|
||||
{ phone: admin.phone, name: "e2e admin", role: "ADMIN", passwordHash: await hashPassword(admin.password) },
|
||||
{ phone: editor.phone, name: "e2e editor", role: "EDITOR", passwordHash: await hashPassword(editor.password) },
|
||||
],
|
||||
});
|
||||
});
|
||||
|
||||
afterAll(async () => {
|
||||
await prisma.adminUser.deleteMany({ where: { email: { in: [admin.email, editor.email] } } });
|
||||
await prisma.adminUser.deleteMany({ where: { phone: { in: [admin.phone, editor.phone] } } });
|
||||
await app.close();
|
||||
});
|
||||
|
||||
@@ -63,6 +63,17 @@ describe("API (e2e)", () => {
|
||||
await http().get("/api/public/categories/no-such-category").expect(404);
|
||||
});
|
||||
|
||||
it("returns a product page with related products, 404 for unknown slugs", async () => {
|
||||
const { body: refs } = await http().get("/api/public/products").expect(200);
|
||||
expect(refs.length).toBeGreaterThan(0);
|
||||
const res = await http().get(`/api/public/products/${refs[0].slug}`).expect(200);
|
||||
expect(res.body.category.slug).toBeTruthy();
|
||||
expect(Array.isArray(res.body.attributes)).toBe(true);
|
||||
expect(res.body.related.length).toBeLessThanOrEqual(4);
|
||||
expect(res.body.related.some((r: { slug: string }) => r.slug === refs[0].slug)).toBe(false);
|
||||
await http().get("/api/public/products/no-such-product").expect(404);
|
||||
});
|
||||
|
||||
it("validates the contact form", async () => {
|
||||
const res = await http().post("/api/public/contact").send({ name: "", org: "x", phone: "abc" }).expect(400);
|
||||
expect(res.body.issues.map((i: { path: string }) => i.path).sort()).toEqual(["name", "phone"]);
|
||||
@@ -81,7 +92,7 @@ describe("API (e2e)", () => {
|
||||
describe("auth", () => {
|
||||
it("rejects admin routes without a token and wrong passwords", async () => {
|
||||
await http().get("/api/admin/dashboard").expect(401);
|
||||
await http().post("/api/admin/auth/login").send({ email: admin.email, password: "wrong-password" }).expect(401);
|
||||
await http().post("/api/admin/auth/login").send({ phone: admin.phone, password: "wrong-password" }).expect(401);
|
||||
});
|
||||
|
||||
it("sets an httpOnly refresh cookie and rotates it", async () => {
|
||||
|
||||
Reference in New Issue
Block a user