Skip to main content
Glama
block-builder.js4.98 kB
import { randomUUID } from 'crypto'; import { QuestionType, questionHasOptions } from '../models/form-config'; /** * Maps internal QuestionType → TallyBlockType. * The mapping is based on the subset of types currently supported by the create_form tool. * When new question types are introduced, extend this mapping accordingly. */ function mapQuestionTypeToBlockType(type) { switch (type) { case QuestionType.TEXT: return 'INPUT_TEXT'; case QuestionType.EMAIL: return 'INPUT_EMAIL'; case QuestionType.NUMBER: return 'INPUT_NUMBER'; case QuestionType.PHONE: return 'INPUT_PHONE_NUMBER'; case QuestionType.URL: return 'INPUT_LINK'; case QuestionType.DATE: return 'INPUT_DATE'; case QuestionType.TIME: return 'INPUT_TIME'; case QuestionType.TEXTAREA: return 'TEXTAREA'; case QuestionType.DROPDOWN: return 'DROPDOWN'; case QuestionType.CHECKBOXES: return 'CHECKBOXES'; case QuestionType.MULTIPLE_CHOICE: return 'MULTIPLE_CHOICE'; case QuestionType.LINEAR_SCALE: return 'LINEAR_SCALE'; case QuestionType.RATING: return 'RATING'; case QuestionType.FILE: return 'FILE_UPLOAD'; case QuestionType.SIGNATURE: return 'SIGNATURE'; default: // Fallback to simple text input; adjust as mapping grows. return 'INPUT_TEXT'; } } /** * Generate a FORM_TITLE block with the provided title. */ export function createFormTitleBlock(title) { return { uuid: randomUUID(), type: 'FORM_TITLE', groupUuid: randomUUID(), groupType: 'TEXT', title, payload: { html: title, }, }; } /** * Generate a Tally block from a QuestionConfig definition. * This keeps the payload intentionally minimal for MVP support – additional * field-specific properties (validations, formatting, etc.) should be appended * in future iterations as downstream tasks address advanced mapping. */ export function createQuestionBlocks(question) { const blocks = []; const groupUuid = randomUUID(); // Title block for question label blocks.push({ uuid: randomUUID(), type: 'TITLE', groupUuid, groupType: 'QUESTION', title: question.label, payload: { html: question.label, }, }); // Input block const blockType = mapQuestionTypeToBlockType(question.type); const payload = { isRequired: question.required ?? false, placeholder: 'placeholder' in question && question.placeholder ? question.placeholder : '', }; if (questionHasOptions(question)) { payload.options = question.options.map((opt) => ({ id: randomUUID(), text: opt.text ?? opt.value ?? opt.id ?? String(opt), })); } // For choice-based questions where Tally expects each option as its own block if (questionHasOptions(question) && ['DROPDOWN', 'MULTIPLE_CHOICE', 'CHECKBOXES'].includes(blockType)) { const optionBlocks = []; const optionGroupUuid = randomUUID(); question.options.forEach((opt, idx) => { let optionType; switch (blockType) { case 'DROPDOWN': optionType = 'DROPDOWN_OPTION'; break; case 'MULTIPLE_CHOICE': optionType = 'MULTIPLE_CHOICE_OPTION'; break; case 'CHECKBOXES': optionType = 'CHECKBOX'; break; default: optionType = 'DROPDOWN_OPTION'; } optionBlocks.push({ uuid: randomUUID(), type: optionType, // cast for index signature; runtime value is correct per docs groupUuid: optionGroupUuid, groupType: blockType, title: opt.text ?? opt.value ?? String(opt), payload: { index: idx, text: opt.text ?? opt.value ?? String(opt), }, }); }); blocks.push(...optionBlocks); return blocks; } blocks.push({ uuid: randomUUID(), type: blockType, groupUuid, groupType: blockType, title: question.label, payload, }); return blocks; } /** * Build the full blocks array for a FormConfig – title block first, followed * by field blocks mirroring the order of the input questions array. */ export function buildBlocksForForm(formConfig) { const blocks = []; blocks.push(createFormTitleBlock(formConfig.title)); formConfig.questions.forEach((q) => { blocks.push(...createQuestionBlocks(q)); }); return blocks; }

Latest Blog Posts

MCP directory API

We provide all the information about MCP servers via our MCP API.

curl -X GET 'https://glama.ai/api/mcp/v1/servers/learnwithcc/tally-mcp'

If you have feedback or need assistance with the MCP directory API, please join our Discord server