const MONDAY_API = "https://api.monday.com/v2"; const MONDAY_FILE_API = "https://api.monday.com/v2/file"; type MondayColumn = { id: string; title: string; type: string }; async function gql(token: string, query: string, variables?: Record) { const res = await fetch(MONDAY_API, { method: "POST", headers: { "Content-Type": "application/json", Authorization: token, }, body: JSON.stringify({ query, variables }), }); if (!res.ok) { const text = await res.text().catch(() => ""); throw new Error(`Monday.com API ${res.status}: ${text}`); } const json = await res.json(); if (json.errors?.length) { throw new Error(`Monday.com GraphQL: ${json.errors[0].message}`); } return json.data; } export async function fetchBoardColumns(token: string, boardId: string): Promise { const data = await gql(token, ` query ($boardId: [ID!]!) { boards(ids: $boardId) { columns { id title type } } } `, { boardId: [boardId] }); return data?.boards?.[0]?.columns ?? []; } export async function createItem( token: string, boardId: string, itemName: string, columnValues: Record ): Promise { const data = await gql(token, ` mutation ($boardId: ID!, $itemName: String!, $columnValues: JSON!) { create_item( board_id: $boardId, item_name: $itemName, column_values: $columnValues, create_labels_if_missing: true ) { id } } `, { boardId, itemName, columnValues: JSON.stringify(columnValues), }); return String(data.create_item.id); } export async function updateItem( token: string, boardId: string, itemId: string, columnValues: Record ): Promise { await gql(token, ` mutation ($boardId: ID!, $itemId: ID!, $columnValues: JSON!) { change_multiple_column_values( board_id: $boardId, item_id: $itemId, column_values: $columnValues, create_labels_if_missing: true ) { id } } `, { boardId, itemId, columnValues: JSON.stringify(columnValues), }); } export async function uploadFileToItem( token: string, itemId: string, columnId: string, fileBuffer: Buffer, fileName: string ): Promise { const query = `mutation ($file: File!) { add_file_to_column(file: $file, item_id: ${itemId}, column_id: "${columnId}") { id } }`; const form = new FormData(); form.append("query", query); form.append("variables[file]", new Blob([new Uint8Array(fileBuffer)]), fileName); const res = await fetch(MONDAY_FILE_API, { method: "POST", headers: { Authorization: token }, body: form, }); if (!res.ok) { const text = await res.text().catch(() => ""); throw new Error(`Monday.com file upload ${res.status}: ${text}`); } } type ItemColumnValue = { id: string; text: string; value: string | null }; export async function readItem( token: string, itemId: string ): Promise<{ name: string; columnValues: ItemColumnValue[] } | null> { const data = await gql(token, ` query ($itemId: [ID!]!) { items(ids: $itemId) { name column_values { id text value } } } `, { itemId: [itemId] }); const item = data?.items?.[0]; if (!item) return null; return { name: item.name, columnValues: item.column_values.map((cv: { id: string; text: string; value: string | null }) => ({ id: cv.id, text: cv.text, value: cv.value, })), }; } export function mapCardToColumnValues( card: Record, columnMap: Record ): Record { const values: Record = {}; for (const [cardField, colId] of Object.entries(columnMap)) { if (!colId || !cardField) continue; const val = card[cardField]; if (val === null || val === undefined) continue; values[colId] = String(val); } return values; } export function mapItemToCardFields( columnValues: ItemColumnValue[], columnMap: Record ): Record { const reverseMap: Record = {}; for (const [cardField, colId] of Object.entries(columnMap)) { reverseMap[colId] = cardField; } const cardData: Record = {}; for (const cv of columnValues) { const cardField = reverseMap[cv.id]; if (cardField && cv.text) { cardData[cardField] = cv.text; } } return cardData; } export async function createWebhookSubscription( token: string, boardId: string, callbackUrl: string ): Promise { const data = await gql(token, ` mutation ($boardId: ID!, $url: String!) { create_webhook(board_id: $boardId, url: $url, event: change_column_value) { id } } `, { boardId, url: callbackUrl }); return String(data.create_webhook.id); } export async function deleteWebhookSubscription( token: string, webhookId: string ): Promise { await gql(token, ` mutation ($webhookId: ID!) { delete_webhook(id: $webhookId) { id } } `, { webhookId }); }