REFACTORED - Flatten portfolio category routes to /portfolio/[slug]
Merge the category and project routes under a single /portfolio/[slug] segment via resolvePortfolioSlug (category wins over project on a slug clash). Removes the /portfolio/category/... prefix from links, redirect, and sitemap. Adds resolver integration tests.
This commit is contained in:
@@ -8,6 +8,7 @@ import {
|
||||
getAdminPortfolioProjects,
|
||||
getPublishedPortfolioProjectBySlug,
|
||||
getPublishedPortfolioProjects,
|
||||
resolvePortfolioSlug,
|
||||
} from "@/lib/portfolio";
|
||||
import { eq } from "drizzle-orm";
|
||||
|
||||
@@ -124,6 +125,44 @@ describe("published projects", () => {
|
||||
});
|
||||
});
|
||||
|
||||
describe("resolvePortfolioSlug", () => {
|
||||
it("resolves an active category slug to a category", async () => {
|
||||
await createCategory({ slug: "web", isActive: true });
|
||||
const resolved = await resolvePortfolioSlug("web");
|
||||
expect(resolved?.kind).toBe("category");
|
||||
expect(resolved?.kind === "category" && resolved.category.slug).toBe("web");
|
||||
});
|
||||
|
||||
it("resolves a published project slug to a project", async () => {
|
||||
const cat = await createCategory({ isActive: true });
|
||||
await createProject({ categoryId: cat.id, slug: "my-project", isPublished: true });
|
||||
const resolved = await resolvePortfolioSlug("my-project");
|
||||
expect(resolved?.kind).toBe("project");
|
||||
expect(resolved?.kind === "project" && resolved.project.slug).toBe("my-project");
|
||||
});
|
||||
|
||||
it("prefers the category when a category and a project share a slug", async () => {
|
||||
const cat = await createCategory({ slug: "shared", isActive: true });
|
||||
await createProject({ categoryId: cat.id, slug: "shared", isPublished: true });
|
||||
const resolved = await resolvePortfolioSlug("shared");
|
||||
expect(resolved?.kind).toBe("category");
|
||||
});
|
||||
|
||||
it("ignores an inactive category and falls back to a matching project", async () => {
|
||||
// An inactive category named "hidden" must not shadow a published project "hidden".
|
||||
await createCategory({ slug: "hidden", isActive: false });
|
||||
const activeCat = await createCategory({ isActive: true });
|
||||
await createProject({ categoryId: activeCat.id, slug: "hidden", isPublished: true });
|
||||
const resolved = await resolvePortfolioSlug("hidden");
|
||||
expect(resolved?.kind).toBe("project");
|
||||
expect(resolved?.kind === "project" && resolved.project.slug).toBe("hidden");
|
||||
});
|
||||
|
||||
it("returns null for an unknown slug", async () => {
|
||||
expect(await resolvePortfolioSlug("does-not-exist")).toBeNull();
|
||||
});
|
||||
});
|
||||
|
||||
describe("referential integrity", () => {
|
||||
it("restricts deleting a category that still has projects", async () => {
|
||||
const cat = await createCategory();
|
||||
|
||||
Reference in New Issue
Block a user