Add folder UI for routines and skills

This commit is contained in:
Dotta committed 2026-07-05 11:39:02 +00:00
1 parent ef86752e46
commit 5dbd390710
10 files changed
+1597 -40

No files matched your search

@@ -0,0 +1,74 @@
import express from "express";
import request from "supertest";
import { beforeEach, describe, expect, it, vi } from "vitest";
const mockFolderService = vi.hoisted(() => ({
list: vi.fn(),
create: vi.fn(),
update: vi.fn(),
moveFolder: vi.fn(),
moveItem: vi.fn(),
deleteFolder: vi.fn(),
}));
const mockLogActivity = vi.hoisted(() => vi.fn());
vi.mock("../services/index.js", () => ({
folderService: () => mockFolderService,
logActivity: mockLogActivity,
}));
async function createApp() {
vi.resetModules();
const [{ errorHandler }, { folderRoutes }] = await Promise.all([
import("../middleware/index.js") as Promise<typeof import("../middleware/index.js")>,
import("../routes/folders.js") as Promise<typeof import("../routes/folders.js")>,
]);
const app = express();
app.use(express.json());
app.use((req, _res, next) => {
(req as any).actor = {
type: "board",
userId: "user-1",
companyIds: ["company-1"],
source: "session",
isInstanceAdmin: false,
};
next();
});
app.use("/api", folderRoutes({} as any));
app.use(errorHandler);
return app;
}
describe("folder routes", () => {
beforeEach(() => {
for (const mock of Object.values(mockFolderService)) mock.mockReset();
mockLogActivity.mockReset();
});
it("routes item moves to the item move handler before the folder reorder route", async () => {
mockFolderService.moveItem.mockResolvedValue({
kind: "routine",
itemId: "11111111-1111-4111-8111-111111111111",
folderId: "22222222-2222-4222-8222-222222222222",
});
const app = await createApp();
const res = await request(app)
.post("/api/companies/company-1/folders/items/move")
.send({
kind: "routine",
itemId: "11111111-1111-4111-8111-111111111111",
folderId: "22222222-2222-4222-8222-222222222222",
});
expect(res.status).toBe(200);
expect(mockFolderService.moveItem).toHaveBeenCalledWith("company-1", {
kind: "routine",
itemId: "11111111-1111-4111-8111-111111111111",
folderId: "22222222-2222-4222-8222-222222222222",
});
expect(mockFolderService.moveFolder).not.toHaveBeenCalled();
});
});
+19 -19
View File
@@ -71,6 +71,25 @@ export function folderRoutes(db: Db) {
res.json(updated);
});
router.post("/companies/:companyId/folders/items/move", validate(moveFolderItemSchema), async (req, res) => {
const companyId = req.params.companyId as string;
assertCompanyAccess(req, companyId);
const moved = await svc.moveItem(companyId, req.body);
const actor = getActorInfo(req);
await logActivity(db, {
companyId,
actorType: actor.actorType,
actorId: actor.actorId,
agentId: actor.agentId,
runId: actor.runId,
action: "folder.item_moved",
entityType: req.body.kind === "routine" ? "routine" : "company_skill",
entityId: moved.itemId,
details: { kind: moved.kind, folderId: moved.folderId },
});
res.json(moved);
});
router.post("/companies/:companyId/folders/:folderId/move", validate(moveFolderSchema), async (req, res) => {
const companyId = req.params.companyId as string;
const folderId = req.params.folderId as string;
@@ -95,25 +114,6 @@ export function folderRoutes(db: Db) {
res.json(updated);
});
router.post("/companies/:companyId/folders/items/move", validate(moveFolderItemSchema), async (req, res) => {
const companyId = req.params.companyId as string;
assertCompanyAccess(req, companyId);
const moved = await svc.moveItem(companyId, req.body);
const actor = getActorInfo(req);
await logActivity(db, {
companyId,
actorType: actor.actorType,
actorId: actor.actorId,
agentId: actor.agentId,
runId: actor.runId,
action: "folder.item_moved",
entityType: req.body.kind === "routine" ? "routine" : "company_skill",
entityId: moved.itemId,
details: { kind: moved.kind, folderId: moved.folderId },
});
res.json(moved);
});
router.delete("/companies/:companyId/folders/:folderId", async (req, res) => {
const companyId = req.params.companyId as string;
const folderId = req.params.folderId as string;