/** * TASK-0106 — form builder routes (workspace-scoped, database-bound). * * GET /databases/:databaseId/forms list forms on a db * POST /databases/:databaseId/forms create (questions inline) * GET /forms/:formId detail + properties * PATCH /forms/:formId update / publish / close * DELETE /forms/:formId soft delete (slug release) * GET /forms/:formId/questions ordered question list * POST /forms/:formId/questions add a question * PATCH /forms/:formId/questions/:qid update a question * DELETE /forms/:formId/questions/:qid delete (answers retained) * POST /forms/:formId/questions/reorder { fromId, toId? } * * Permissions live in the services (VIEW to read, EDIT on the DATABASE to * write — the frontend is never the gate). */ import { Body, Controller, Delete, Get, Inject, Param, Patch, Post, Req } from "@nestjs/common"; import type { Request } from "express"; import { FormsService } from "./forms.service.js"; import { QuestionsService } from "./questions.service.js"; import { ZodValidate } from "../../common/pipes/zod-validation.pipe.js"; import { requireUser } from "../../common/types.js"; import { CreateFormDto, UpdateFormDto, type CreateFormInput, type UpdateFormInput } from "./form.dto.js"; import { CreateQuestionDto, ReorderQuestionsDto, UpdateQuestionDto, type CreateQuestionInput, type ReorderQuestionsInput, type UpdateQuestionInput } from "./question.dto.js"; @Controller() export class FormsController { constructor( @Inject(FormsService) private readonly forms: FormsService, @Inject(QuestionsService) private readonly questions: QuestionsService ) {} // ---------- form CRUD (database-scoped create, id-scoped rest) ---------- @Get("databases/:databaseId/forms") list(@Param("databaseId") databaseId: string, @Req() req: Request) { return this.forms.listByDatabase(databaseId, requireUser(req)); } @Post("databases/:databaseId/forms") create( @Param("databaseId") databaseId: string, @Body(ZodValidate(CreateFormDto)) body: CreateFormInput, @Req() req: Request ) { return this.forms.create(databaseId, requireUser(req), body, req); } @Get("forms/:formId") get(@Param("formId") formId: string, @Req() req: Request) { return this.forms.get(formId, requireUser(req)); } @Patch("forms/:formId") update( @Param("formId") formId: string, @Body(ZodValidate(UpdateFormDto)) body: UpdateFormInput, @Req() req: Request ) { return this.forms.update(formId, requireUser(req), body, req); } @Delete("forms/:formId") remove(@Param("formId") formId: string, @Req() req: Request) { return this.forms.remove(formId, requireUser(req), req); } // ---------- question ops (TASK-0106 §3) ---------- @Get("forms/:formId/questions") listQuestions(@Param("formId") formId: string, @Req() req: Request) { requireUser(req); // auth (the list itself is the form's data) return this.questions.list(formId); } @Post("forms/:formId/questions") createQuestion( @Param("formId") formId: string, @Body(ZodValidate(CreateQuestionDto)) body: CreateQuestionInput, @Req() req: Request ) { return this.questions.create(formId, requireUser(req), body); } @Patch("forms/:formId/questions/:qid") updateQuestion( @Param("formId") formId: string, @Param("qid") qid: string, @Body(ZodValidate(UpdateQuestionDto)) body: UpdateQuestionInput, @Req() req: Request ) { return this.questions.update(formId, qid, requireUser(req), body); } @Delete("forms/:formId/questions/:qid") deleteQuestion(@Param("formId") formId: string, @Param("qid") qid: string, @Req() req: Request) { return this.questions.delete(formId, qid, requireUser(req)); } @Post("forms/:formId/questions/reorder") reorder( @Param("formId") formId: string, @Body(ZodValidate(ReorderQuestionsDto)) body: ReorderQuestionsInput, @Req() req: Request ) { return this.questions.reorder(formId, requireUser(req), body); } }