// ============================================================================ // FORM ROUTER - Area do Cliente Avanzato // ============================================================================ // Routers para gestao de formularios e respostas // ============================================================================ import { z } from "zod"; import { createRouter, publicQuery, authedQuery } from "./middleware"; import { getDb } from "./queries/connection"; import { clientForms, formSections, formQuestions, formAnswers, formSubmissions } from "../db/schema"; import { eq, desc, and } from "drizzle-orm"; // Gerar codigo aleatorio de 8 caracteres function generateCode(): string { return Math.random().toString(36).substring(2, 10).toUpperCase(); } export const formRouter = createRouter({ // ========================================================================== // ADMIN - Gerenciamento de Formularios // ========================================================================== // Listar todos os formularios list: publicQuery.query(async () => { const db = getDb(); const forms = await db.select().from(clientForms).orderBy(desc(clientForms.createdAt)); return forms; }), // Criar novo formulario create: publicQuery .input( z.object({ title: z.string().min(1), description: z.string().optional(), clientName: z.string().min(1), clientEmail: z.string().email().optional(), }) ) .mutation(async ({ input }) => { const db = getDb(); const code = generateCode(); await db.insert(clientForms).values({ title: input.title, description: input.description || "", clientName: input.clientName, clientEmail: input.clientEmail || "", status: "active", accessCode: code, createdBy: "Avanzato", }); return { code, message: "Formulario criado com sucesso" }; }), // Atualizar status (active/inactive/archived) updateStatus: publicQuery .input( z.object({ accessCode: z.string(), status: z.enum(["active", "inactive", "archived"]), }) ) .mutation(async ({ input }) => { const db = getDb(); await db .update(clientForms) .set({ status: input.status }) .where(eq(clientForms.accessCode, input.accessCode)); return { message: "Status atualizado" }; }), // Excluir formulario delete: publicQuery .input(z.object({ accessCode: z.string() })) .mutation(async ({ input }) => { const db = getDb(); // Primeiro excluir respostas e perguntas await db.delete(formAnswers).where(eq(formAnswers.formId, input.accessCode)); await db.delete(formQuestions).where(eq(formQuestions.formId, input.accessCode)); await db.delete(formSections).where(eq(formSections.formId, input.accessCode)); await db.delete(formSubmissions).where(eq(formSubmissions.formId, input.accessCode)); await db.delete(clientForms).where(eq(clientForms.accessCode, input.accessCode)); return { message: "Formulario excluido" }; }), // Renovar codigo de acesso regenerateCode: publicQuery .input(z.object({ accessCode: z.string() })) .mutation(async ({ input }) => { const db = getDb(); const newCode = generateCode(); await db .update(clientForms) .set({ accessCode: newCode }) .where(eq(clientForms.accessCode, input.accessCode)); return { newCode }; }), // ========================================================================== // PUBLICO - Acesso ao Formulario via Codigo // ========================================================================== // Buscar formulario por codigo (publico) getByCode: publicQuery .input(z.object({ code: z.string() })) .query(async ({ input }) => { const db = getDb(); const [form] = await db .select() .from(clientForms) .where(eq(clientForms.accessCode, input.code)); if (!form) return null; if (form.status !== "active") return { ...form, _blocked: true }; // Buscar perguntas agrupadas por secao const questions = await db .select() .from(formQuestions) .where(eq(formQuestions.formId, input.code)) .orderBy(formQuestions.id); const sections = await db .select() .from(formSections) .where(eq(formSections.formId, input.code)) .orderBy(formSections.id); return { ...form, questions, sections }; }), // Submeter respostas do formulario submit: publicQuery .input( z.object({ formId: z.string(), code: z.string(), answers: z.array( z.object({ questionId: z.string(), answer: z.string(), }) ), submittedBy: z.string().optional(), submittedByEmail: z.string().optional(), }) ) .mutation(async ({ input }) => { const db = getDb(); // Verificar se formulario esta ativo const [form] = await db .select() .from(clientForms) .where(eq(clientForms.accessCode, input.code)); if (!form || form.status !== "active") { throw new Error("Formulario nao encontrado ou inativo"); } // Inserir submissao await db.insert(formSubmissions).values({ formId: input.formId, submittedBy: input.submittedBy || "Anonimo", submittedByEmail: input.submittedByEmail || "", }); // Inserir respostas for (const a of input.answers) { await db.insert(formAnswers).values({ formId: input.formId, questionId: a.questionId, answer: a.answer, }); } return { message: "Respostas submetidas com sucesso" }; }), // ========================================================================== // ADMIN - Visualizar Respostas // ========================================================================== // Listar submissoes de um formulario getSubmissions: publicQuery .input(z.object({ accessCode: z.string() })) .query(async ({ input }) => { const db = getDb(); const submissions = await db .select() .from(formSubmissions) .where(eq(formSubmissions.formId, input.accessCode)) .orderBy(desc(formSubmissions.createdAt)); return submissions; }), // Obter todas as respostas de uma submissao getAnswers: publicQuery .input(z.object({ formId: z.string() })) .query(async ({ input }) => { const db = getDb(); const answers = await db .select() .from(formAnswers) .where(eq(formAnswers.formId, input.formId)) .orderBy(formAnswers.id); return answers; }), });