From 4431a1a48486e2049c36873fe9e606468e639f98 Mon Sep 17 00:00:00 2001 From: Martin Yankov <23098926+Lutherwaves@users.noreply.github.com> Date: Sat, 20 Dec 2025 04:59:08 +0200 Subject: [PATCH 01/10] fix(helm): add custom egress rules to realtime network policy (#2481) The realtime service network policy was missing the custom egress rules section that allows configuration of additional egress rules via values.yaml. This caused the realtime pods to be unable to connect to external databases (e.g., PostgreSQL on port 5432) when using external database configurations. The app network policy already had this section, but the realtime network policy was missing it, creating an inconsistency and preventing the realtime service from accessing external databases configured via networkPolicy.egress values. This fix adds the same custom egress rules template section to the realtime network policy, matching the app network policy behavior and allowing users to configure database connectivity via values.yaml. --- helm/sim/templates/networkpolicy.yaml | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/helm/sim/templates/networkpolicy.yaml b/helm/sim/templates/networkpolicy.yaml index deac5a5dba..7ef8697417 100644 --- a/helm/sim/templates/networkpolicy.yaml +++ b/helm/sim/templates/networkpolicy.yaml @@ -141,6 +141,10 @@ spec: ports: - protocol: TCP port: 443 + # Allow custom egress rules + {{- with .Values.networkPolicy.egress }} + {{- toYaml . | nindent 2 }} + {{- end }} {{- end }} {{- if .Values.postgresql.enabled }} From 93b753148f0fec037889911e735e193617f9bc61 Mon Sep 17 00:00:00 2001 From: shubhamxshah Date: Tue, 30 Dec 2025 16:07:26 +0530 Subject: [PATCH 02/10] v1 works --- apps/sim/app/api/tools/monday/boards/route.ts | 53 +++++ .../sim/app/api/tools/monday/columns/route.ts | 59 +++++ apps/sim/app/api/tools/monday/groups/route.ts | 55 +++++ .../api/tools/monday/status-options/route.ts | 97 ++++++++ .../selector-combobox/selector-combobox.tsx | 52 ++++- .../sub-block/hooks/use-depends-on-gate.ts | 13 ++ .../editor/components/sub-block/sub-block.tsx | 21 +- .../file-selector/file-selector-input.tsx | 61 ++++- .../hooks/use-workflow-execution.ts | 7 +- apps/sim/blocks/blocks/monday.ts | 184 +++++++++++++++ apps/sim/blocks/registry.ts | 2 + apps/sim/components/icons.tsx | 17 ++ apps/sim/hooks/selectors/registry.ts | 98 ++++++++ apps/sim/hooks/selectors/resolution.ts | 20 ++ apps/sim/hooks/selectors/types.ts | 8 + .../lib/workflows/executor/execution-core.ts | 14 ++ apps/sim/serializer/index.ts | 18 +- apps/sim/tools/monday/create_item.ts | 114 ++++++++++ apps/sim/tools/monday/get_item.ts | 87 ++++++++ apps/sim/tools/monday/graphql.ts | 210 ++++++++++++++++++ apps/sim/tools/monday/index.ts | 4 + apps/sim/tools/monday/list_items.ts | 96 ++++++++ apps/sim/tools/monday/types.ts | 145 ++++++++++++ apps/sim/tools/monday/update_item.ts | 104 +++++++++ apps/sim/tools/registry.ts | 10 + apps/sim/triggers/monday/column_changed.ts | 129 +++++++++++ apps/sim/triggers/monday/index.ts | 2 + apps/sim/triggers/monday/new_item.ts | 104 +++++++++ apps/sim/triggers/registry.ts | 3 + bun.lock | 1 - 30 files changed, 1775 insertions(+), 13 deletions(-) create mode 100644 apps/sim/app/api/tools/monday/boards/route.ts create mode 100644 apps/sim/app/api/tools/monday/columns/route.ts create mode 100644 apps/sim/app/api/tools/monday/groups/route.ts create mode 100644 apps/sim/app/api/tools/monday/status-options/route.ts create mode 100644 apps/sim/blocks/blocks/monday.ts create mode 100644 apps/sim/tools/monday/create_item.ts create mode 100644 apps/sim/tools/monday/get_item.ts create mode 100644 apps/sim/tools/monday/graphql.ts create mode 100644 apps/sim/tools/monday/index.ts create mode 100644 apps/sim/tools/monday/list_items.ts create mode 100644 apps/sim/tools/monday/types.ts create mode 100644 apps/sim/tools/monday/update_item.ts create mode 100644 apps/sim/triggers/monday/column_changed.ts create mode 100644 apps/sim/triggers/monday/index.ts create mode 100644 apps/sim/triggers/monday/new_item.ts diff --git a/apps/sim/app/api/tools/monday/boards/route.ts b/apps/sim/app/api/tools/monday/boards/route.ts new file mode 100644 index 0000000000..11a212acae --- /dev/null +++ b/apps/sim/app/api/tools/monday/boards/route.ts @@ -0,0 +1,53 @@ +import { NextResponse } from 'next/server' +import { createLogger } from '@sim/logger' +import { generateRequestId } from '@/lib/core/utils/request' +import { executeMondayQuery, QUERIES } from '@/tools/monday/graphql' + +export const dynamic = 'force-dynamic' + +const logger = createLogger('MondayBoardsAPI') + +interface MondayBoard { + id: string + name: string + description?: string + board_kind: string + state: string +} + +export async function POST(request: Request) { + try { + const requestId = generateRequestId() + const body = await request.json() + const { apiKey } = body + + if (!apiKey) { + logger.error('Missing API key in request') + return NextResponse.json({ error: 'API key is required' }, { status: 400 }) + } + + logger.info('Fetching Monday.com boards', { requestId }) + + const data = await executeMondayQuery<{ boards: MondayBoard[] }>(apiKey, { + query: QUERIES.GET_BOARDS, + }) + + const boards = (data.boards || []) + .filter((board) => board.state === 'active') + .map((board) => ({ + id: board.id, + name: board.name, + description: board.description, + kind: board.board_kind, + })) + + logger.info(`Successfully fetched ${boards.length} Monday.com boards`, { requestId }) + return NextResponse.json({ items: boards }) + } catch (error) { + logger.error('Error fetching Monday.com boards:', error) + return NextResponse.json( + { error: 'Failed to retrieve Monday.com boards', details: (error as Error).message }, + { status: 500 } + ) + } +} diff --git a/apps/sim/app/api/tools/monday/columns/route.ts b/apps/sim/app/api/tools/monday/columns/route.ts new file mode 100644 index 0000000000..b5a7f6981a --- /dev/null +++ b/apps/sim/app/api/tools/monday/columns/route.ts @@ -0,0 +1,59 @@ +import { NextResponse } from 'next/server' +import { createLogger } from '@sim/logger' +import { generateRequestId } from '@/lib/core/utils/request' +import { executeMondayQuery, QUERIES } from '@/tools/monday/graphql' + +export const dynamic = 'force-dynamic' + +const logger = createLogger('MondayColumnsAPI') + +interface MondayColumn { + id: string + title: string + type: string + settings_str?: string +} + +export async function POST(request: Request) { + try { + const requestId = generateRequestId() + const body = await request.json() + const { apiKey, boardId } = body + + if (!apiKey) { + logger.error('Missing API key in request') + return NextResponse.json({ error: 'API key is required' }, { status: 400 }) + } + + if (!boardId) { + logger.error('Missing board ID in request') + return NextResponse.json({ error: 'Board ID is required' }, { status: 400 }) + } + + logger.info('Fetching Monday.com columns', { requestId, boardId }) + + const data = await executeMondayQuery<{ boards: Array<{ columns: MondayColumn[] }> }>( + apiKey, + { + query: QUERIES.GET_BOARD_COLUMNS, + variables: { boardId: [parseInt(boardId, 10)] }, + } + ) + + const columns = data.boards?.[0]?.columns || [] + const formattedColumns = columns.map((col) => ({ + id: col.id, + name: col.title, + type: col.type, + })) + + logger.info(`Successfully fetched ${formattedColumns.length} columns`, { requestId }) + return NextResponse.json({ items: formattedColumns }) + } catch (error) { + logger.error('Error fetching Monday.com columns:', error) + return NextResponse.json( + { error: 'Failed to retrieve columns', details: (error as Error).message }, + { status: 500 } + ) + } +} diff --git a/apps/sim/app/api/tools/monday/groups/route.ts b/apps/sim/app/api/tools/monday/groups/route.ts new file mode 100644 index 0000000000..60c56aaaac --- /dev/null +++ b/apps/sim/app/api/tools/monday/groups/route.ts @@ -0,0 +1,55 @@ +import { NextResponse } from 'next/server' +import { createLogger } from '@sim/logger' +import { generateRequestId } from '@/lib/core/utils/request' +import { executeMondayQuery, QUERIES } from '@/tools/monday/graphql' + +export const dynamic = 'force-dynamic' + +const logger = createLogger('MondayGroupsAPI') + +interface MondayGroup { + id: string + title: string + color: string +} + +export async function POST(request: Request) { + try { + const requestId = generateRequestId() + const body = await request.json() + const { apiKey, boardId } = body + + if (!apiKey || !boardId) { + return NextResponse.json( + { error: 'API key and board ID are required' }, + { status: 400 } + ) + } + + logger.info('Fetching Monday.com groups', { requestId, boardId }) + + const data = await executeMondayQuery<{ boards: Array<{ groups: MondayGroup[] }> }>( + apiKey, + { + query: QUERIES.GET_BOARD_GROUPS, + variables: { boardId: [parseInt(boardId, 10)] }, + } + ) + + const groups = data.boards?.[0]?.groups || [] + const formattedGroups = groups.map((group) => ({ + id: group.id, + name: group.title, + color: group.color, + })) + + logger.info(`Successfully fetched ${formattedGroups.length} groups`, { requestId }) + return NextResponse.json({ items: formattedGroups }) + } catch (error) { + logger.error('Error fetching Monday.com groups:', error) + return NextResponse.json( + { error: 'Failed to retrieve groups', details: (error as Error).message }, + { status: 500 } + ) + } +} diff --git a/apps/sim/app/api/tools/monday/status-options/route.ts b/apps/sim/app/api/tools/monday/status-options/route.ts new file mode 100644 index 0000000000..200342b28d --- /dev/null +++ b/apps/sim/app/api/tools/monday/status-options/route.ts @@ -0,0 +1,97 @@ +import { NextResponse } from 'next/server' +import { createLogger } from '@sim/logger' +import { generateRequestId } from '@/lib/core/utils/request' +import { executeMondayQuery, QUERIES } from '@/tools/monday/graphql' + +export const dynamic = 'force-dynamic' + +const logger = createLogger('MondayStatusOptionsAPI') + +interface MondayColumn { + id: string + title: string + type: string + settings_str?: string +} + +interface StatusLabel { + id: string + label: string + color: string +} + +export async function POST(request: Request) { + try { + const requestId = generateRequestId() + const body = await request.json() + const { apiKey, boardId, columnId } = body + + if (!apiKey || !boardId || !columnId) { + return NextResponse.json( + { error: 'API key, board ID, and column ID are required' }, + { status: 400 } + ) + } + + logger.info('Fetching Monday.com status options', { requestId, boardId, columnId }) + + const data = await executeMondayQuery<{ boards: Array<{ columns: MondayColumn[] }> }>( + apiKey, + { + query: QUERIES.GET_COLUMN_SETTINGS, + variables: { + boardId: [parseInt(boardId, 10)], + columnId, + }, + } + ) + + const column = data.boards?.[0]?.columns?.[0] + + if (!column) { + return NextResponse.json({ error: 'Column not found' }, { status: 404 }) + } + + if (column.type !== 'status' && column.type !== 'color') { + return NextResponse.json( + { error: `Column type ${column.type} does not have status options` }, + { status: 400 } + ) + } + + let statusOptions: StatusLabel[] = [] + + if (column.settings_str) { + try { + const settings = JSON.parse(column.settings_str) + const labels = settings.labels || {} + + statusOptions = Object.entries(labels).map(([id, label]: [string, any]) => ({ + id, + label: label.label || label, + color: label.color || '#000000', + })) + } catch (parseError) { + logger.error('Failed to parse column settings', { + error: parseError, + settings_str: column.settings_str, + }) + } + } + + logger.info(`Successfully fetched ${statusOptions.length} status options`, { requestId }) + return NextResponse.json({ + items: statusOptions.map((option) => ({ + id: option.id, + name: option.label, + color: option.color, + })), + }) + } catch (error) { + logger.error('Error fetching Monday.com status options:', error) + return NextResponse.json( + { error: 'Failed to retrieve status options', details: (error as Error).message }, + { status: 500 } + ) + } +} diff --git a/apps/sim/app/workspace/[workspaceId]/w/[workflowId]/components/panel/components/editor/components/sub-block/components/selector-combobox/selector-combobox.tsx b/apps/sim/app/workspace/[workspaceId]/w/[workflowId]/components/panel/components/editor/components/sub-block/components/selector-combobox/selector-combobox.tsx index f72930bdc6..df9096ee9e 100644 --- a/apps/sim/app/workspace/[workspaceId]/w/[workflowId]/components/panel/components/editor/components/sub-block/components/selector-combobox/selector-combobox.tsx +++ b/apps/sim/app/workspace/[workspaceId]/w/[workflowId]/components/panel/components/editor/components/sub-block/components/selector-combobox/selector-combobox.tsx @@ -38,6 +38,46 @@ export function SelectorCombobox({ onOptionChange, allowSearch = true, }: SelectorComboboxProps) { + // For Monday.com selectors, read apiKey and boardId directly from block state + const [apiKeyFromBlock] = useSubBlockValue(blockId, 'apiKey') + const [boardIdFromBlock] = useSubBlockValue(blockId, 'board_id') + const [boardIdCamelFromBlock] = useSubBlockValue(blockId, 'boardId') + const [columnIdFromBlock] = useSubBlockValue(blockId, 'column_id') + const [columnIdCamelFromBlock] = useSubBlockValue(blockId, 'columnId') + + // Merge Monday.com specific values into context if they're missing + const enrichedContext = selectorKey.startsWith('monday.') ? { + ...selectorContext, + apiKey: selectorContext.apiKey || apiKeyFromBlock, + boardId: selectorContext.boardId || boardIdFromBlock || boardIdCamelFromBlock, + columnId: selectorContext.columnId || columnIdFromBlock || columnIdCamelFromBlock, + } : selectorContext + + // For Monday selectors, override disabled if we have apiKey and required dependencies + let actualDisabled = disabled + if (selectorKey.startsWith('monday.')) { + if (selectorKey === 'monday.boards') { + // boards only needs apiKey + actualDisabled = !enrichedContext.apiKey + } else if (selectorKey === 'monday.columns' || selectorKey === 'monday.groups') { + // columns/groups need apiKey AND boardId + actualDisabled = !enrichedContext.apiKey || !enrichedContext.boardId + } else if (selectorKey === 'monday.status-options') { + // status-options need apiKey, boardId, AND columnId + actualDisabled = !enrichedContext.apiKey || !enrichedContext.boardId || !enrichedContext.columnId + } + } + + console.log('[SelectorCombobox RENDER]', { + subBlockId: subBlock.id, + selectorKey, + disabled, + actualDisabled, + hasApiKey: !!enrichedContext.apiKey, + apiKeyFromBlock, + enrichedContext + }) + const [storeValueRaw, setStoreValue] = useSubBlockValue( blockId, subBlock.id @@ -52,11 +92,11 @@ export function SelectorCombobox({ isLoading, error, } = useSelectorOptions(selectorKey, { - context: selectorContext, + context: enrichedContext, search: allowSearch ? searchTerm : undefined, }) const { data: detailOption } = useSelectorOptionDetail(selectorKey, { - context: selectorContext, + context: enrichedContext, detailId: activeValue, }) const optionMap = useSelectorOptionMap(options, detailOption ?? undefined) @@ -89,12 +129,12 @@ export function SelectorCombobox({ const handleSelection = useCallback( (value: string) => { - if (readOnly || disabled) return + if (readOnly || actualDisabled) return setStoreValue(value) setIsEditing(false) onOptionChange?.(value) }, - [setStoreValue, onOptionChange, readOnly, disabled] + [setStoreValue, onOptionChange, readOnly, actualDisabled] ) return ( @@ -104,7 +144,7 @@ export function SelectorCombobox({ subBlockId={subBlock.id} config={subBlock} value={activeValue ?? ''} - disabled={disabled || readOnly} + disabled={actualDisabled || readOnly} isPreview={isPreview} > {({ ref, onDrop, onDragOver }) => ( @@ -127,7 +167,7 @@ export function SelectorCombobox({ } }} placeholder={placeholder || subBlock.placeholder || 'Select an option'} - disabled={disabled || readOnly} + disabled={actualDisabled || readOnly} editable={allowSearch} filterOptions={allowSearch} inputRef={ref as React.RefObject} diff --git a/apps/sim/app/workspace/[workspaceId]/w/[workflowId]/components/panel/components/editor/components/sub-block/hooks/use-depends-on-gate.ts b/apps/sim/app/workspace/[workspaceId]/w/[workflowId]/components/panel/components/editor/components/sub-block/hooks/use-depends-on-gate.ts index 3c145f52fc..2c1583ab83 100644 --- a/apps/sim/app/workspace/[workspaceId]/w/[workflowId]/components/panel/components/editor/components/sub-block/hooks/use-depends-on-gate.ts +++ b/apps/sim/app/workspace/[workspaceId]/w/[workflowId]/components/panel/components/editor/components/sub-block/hooks/use-depends-on-gate.ts @@ -144,9 +144,22 @@ export function useDependsOnGate( const finalDisabled = disabledProp || isPreview || blocked + // Debug logging for dependency issues + if (typeof window !== 'undefined' && allDependsOnFields.includes('apiKey')) { + console.log('[useDependsOnGate Debug]', { + subBlockId: subBlock.id, + allDependsOnFields, + dependencyValuesMap, + depsSatisfied, + blocked, + finalDisabled, + }) + } + return { dependsOn, dependencyValues, + dependencyValuesMap, depsSatisfied, blocked, finalDisabled, diff --git a/apps/sim/app/workspace/[workspaceId]/w/[workflowId]/components/panel/components/editor/components/sub-block/sub-block.tsx b/apps/sim/app/workspace/[workspaceId]/w/[workflowId]/components/panel/components/editor/components/sub-block/sub-block.tsx index 310c3df0dd..a5e6186f9d 100644 --- a/apps/sim/app/workspace/[workspaceId]/w/[workflowId]/components/panel/components/editor/components/sub-block/sub-block.tsx +++ b/apps/sim/app/workspace/[workspaceId]/w/[workflowId]/components/panel/components/editor/components/sub-block/sub-block.tsx @@ -392,7 +392,7 @@ function SubBlockComponent({ // Use dependsOn gating to compute final disabled state // Only pass previewContextValues when in preview mode to avoid format mismatches - const { finalDisabled: gatedDisabled } = useDependsOnGate(blockId, config, { + const { finalDisabled: gatedDisabled, dependencyValuesMap, depsSatisfied } = useDependsOnGate(blockId, config, { disabled, isPreview, previewContextValues: isPreview ? subBlockValues : undefined, @@ -400,6 +400,19 @@ function SubBlockComponent({ const isDisabled = gatedDisabled + // Debug logging for Monday selector issues + if (config.serviceId === 'monday' && typeof window !== 'undefined') { + console.log('[Sub-block Debug]', { + subBlockId: config.id, + type: config.type, + dependsOn: config.dependsOn, + dependencyValuesMap, + depsSatisfied, + gatedDisabled, + isDisabled, + }) + } + /** * Selects and renders the appropriate input component based on config.type. * @@ -656,6 +669,12 @@ function SubBlockComponent({ ) case 'file-selector': + console.log('[SUB-BLOCK] Rendering file-selector', { + subBlockId: config.id, + serviceId: config.serviceId, + isDisabled, + dependsOn: config.dependsOn + }) return ( { if (!isPreview) { + console.log('[FileSelectorInput] Setting value', { + blockId, + subBlockId: subBlock.id, + value, + valueType: typeof value, + }) collaborativeSetSubblockValue(blockId, subBlock.id, value) } }} diff --git a/apps/sim/app/workspace/[workspaceId]/w/[workflowId]/hooks/use-workflow-execution.ts b/apps/sim/app/workspace/[workspaceId]/w/[workflowId]/hooks/use-workflow-execution.ts index 61af9f0624..8cdc7349a8 100644 --- a/apps/sim/app/workspace/[workspaceId]/w/[workflowId]/hooks/use-workflow-execution.ts +++ b/apps/sim/app/workspace/[workspaceId]/w/[workflowId]/hooks/use-workflow-execution.ts @@ -903,7 +903,12 @@ export function useWorkflowExecution() { loops: executionWorkflowState.loops, parallels: executionWorkflowState.parallels, } - : undefined, + : { + blocks: filteredStates, + edges: workflowEdges, + loops: latestWorkflowState.loops || {}, + parallels: latestWorkflowState.parallels || {}, + }, callbacks: { onExecutionStarted: (data) => { logger.info('Server execution started:', data) diff --git a/apps/sim/blocks/blocks/monday.ts b/apps/sim/blocks/blocks/monday.ts new file mode 100644 index 0000000000..efa6889d98 --- /dev/null +++ b/apps/sim/blocks/blocks/monday.ts @@ -0,0 +1,184 @@ +import { MondayIcon } from '@/components/icons' +import type { BlockConfig } from '@/blocks/types' +import { AuthMode } from '@/blocks/types' +import type { MondayResponse } from '@/tools/monday/types' + +export const MondayBlock: BlockConfig = { + type: 'monday', + name: 'Monday', + description: 'Create and manage items on Monday boards', + authMode: AuthMode.ApiKey, + longDescription: + 'Integrate with Monday work management platform. Create items, update column values, list items, and manage your boards programmatically.', + docsLink: 'https://docs.monday.com/api', + category: 'tools', + bgColor: '#FF3D57', + icon: MondayIcon, + subBlocks: [ + { + id: 'operation', + title: 'Operation', + type: 'dropdown', + options: [ + { label: 'Create Item', id: 'monday_create_item' }, + { label: 'Update Item', id: 'monday_update_item' }, + { label: 'Get Item', id: 'monday_get_item' }, + { label: 'List Items', id: 'monday_list_items' }, + ], + value: () => 'monday_create_item', + required: true, + }, + { + id: 'apiKey', + title: 'API Key', + type: 'short-input', + placeholder: 'Enter your Monday.com API key', + password: true, + required: true, + }, + // CREATE ITEM fields + { + id: 'board_id', + title: 'Board', + type: 'file-selector', + serviceId: 'monday', + placeholder: 'Select a Monday.com board', + required: true, + condition: { field: 'operation', value: 'monday_create_item' }, + dependsOn: ['apiKey'], + }, + { + id: 'group_id', + title: 'Group (Optional)', + type: 'file-selector', + serviceId: 'monday', + placeholder: 'Select a group/section (optional)', + required: false, + condition: { field: 'operation', value: 'monday_create_item' }, + dependsOn: ['apiKey', 'board_id'], + }, + { + id: 'item_name', + title: 'Item Name', + type: 'short-input', + placeholder: 'Enter item name', + required: true, + condition: { field: 'operation', value: 'monday_create_item' }, + }, + { + id: 'column_values', + title: 'Column Values (JSON)', + type: 'code', + language: 'json', + placeholder: '{"status": "Working on it", "text": "Example"}', + required: false, + condition: { field: 'operation', value: 'monday_create_item' }, + }, + // UPDATE ITEM fields + { + id: 'item_id', + title: 'Item ID', + type: 'short-input', + placeholder: 'Enter item ID to update', + required: true, + condition: { field: 'operation', value: 'monday_update_item' }, + }, + { + id: 'board_id_update', + title: 'Board ID (Optional)', + type: 'short-input', + canonicalParamId: 'board_id', + placeholder: 'Enter board ID (recommended)', + required: false, + condition: { field: 'operation', value: 'monday_update_item' }, + }, + { + id: 'column_values_update', + title: 'Column Values (JSON)', + type: 'code', + language: 'json', + canonicalParamId: 'column_values', + placeholder: '{"status": "Done", "text": "Updated"}', + required: true, + condition: { field: 'operation', value: 'monday_update_item' }, + }, + // GET ITEM fields + { + id: 'item_id_get', + title: 'Item ID', + type: 'short-input', + canonicalParamId: 'item_id', + placeholder: 'Enter item ID to retrieve', + required: true, + condition: { field: 'operation', value: 'monday_get_item' }, + }, + // LIST ITEMS fields + { + id: 'board_id_list', + title: 'Board', + type: 'file-selector', + serviceId: 'monday', + canonicalParamId: 'board_id', + placeholder: 'Select a board', + required: true, + condition: { field: 'operation', value: 'monday_list_items' }, + dependsOn: ['apiKey'], + }, + { + id: 'group_id_list', + title: 'Group (Optional)', + type: 'file-selector', + serviceId: 'monday', + canonicalParamId: 'group_id', + placeholder: 'Filter by group (optional)', + required: false, + condition: { field: 'operation', value: 'monday_list_items' }, + dependsOn: ['apiKey', 'board_id_list'], + }, + { + id: 'limit', + title: 'Limit', + type: 'slider', + min: 1, + max: 100, + step: 1, + defaultValue: 25, + required: false, + condition: { field: 'operation', value: 'monday_list_items' }, + }, + ], + tools: { + access: [ + 'monday_create_item', + 'monday_update_item', + 'monday_get_item', + 'monday_list_items', + ], + config: { + tool: (params) => { + return params.operation || 'monday_create_item' + }, + params: (inputs) => { + const { operation, ...rest } = inputs + return rest + }, + }, + }, + inputs: { + operation: { type: 'string', description: 'Operation to perform' }, + apiKey: { type: 'string', description: 'Monday.com API key' }, + board_id: { type: 'string', description: 'Board ID' }, + group_id: { type: 'string', description: 'Group/section ID' }, + item_id: { type: 'string', description: 'Item ID' }, + item_name: { type: 'string', description: 'Item name' }, + column_values: { type: 'json', description: 'Column values as JSON' }, + limit: { type: 'number', description: 'Maximum number of items to return' }, + }, + outputs: { + success: { type: 'boolean', description: 'Whether operation succeeded' }, + item: { type: 'json', description: 'Single item object' }, + items: { type: 'array', description: 'Array of items (for list operation)' }, + item_id: { type: 'string', description: 'Item ID' }, + error: { type: 'string', description: 'Error message if failed' }, + }, +} diff --git a/apps/sim/blocks/registry.ts b/apps/sim/blocks/registry.ts index b9b30e5fa7..ee56963102 100644 --- a/apps/sim/blocks/registry.ts +++ b/apps/sim/blocks/registry.ts @@ -59,6 +59,7 @@ import { KnowledgeBlock } from '@/blocks/blocks/knowledge' import { LinearBlock } from '@/blocks/blocks/linear' import { LinkedInBlock } from '@/blocks/blocks/linkedin' import { LinkupBlock } from '@/blocks/blocks/linkup' +import { MondayBlock } from '@/blocks/blocks/monday' import { MailchimpBlock } from '@/blocks/blocks/mailchimp' import { MailgunBlock } from '@/blocks/blocks/mailgun' import { ManualTriggerBlock } from '@/blocks/blocks/manual_trigger' @@ -213,6 +214,7 @@ export const registry: Record = { microsoft_planner: MicrosoftPlannerBlock, microsoft_teams: MicrosoftTeamsBlock, mistral_parse: MistralParseBlock, + monday: MondayBlock, mongodb: MongoDBBlock, mysql: MySQLBlock, neo4j: Neo4jBlock, diff --git a/apps/sim/components/icons.tsx b/apps/sim/components/icons.tsx index 1a6805e5e0..616fb60f8c 100644 --- a/apps/sim/components/icons.tsx +++ b/apps/sim/components/icons.tsx @@ -253,6 +253,23 @@ export function MessagesIcon(props: SVGProps) { ) } +export function MondayIcon(props: SVGProps) { + return ( + + + + + + ) +} + export function NotificationsIcon(props: SVGProps) { return ( = { })) }, }, + 'monday.boards': { + key: 'monday.boards', + staleTime: SELECTOR_STALE, + getQueryKey: ({ context }: SelectorQueryArgs) => [ + 'selectors', + 'monday.boards', + context.apiKey ?? 'none', + ], + enabled: ({ context }) => Boolean(context.apiKey), + fetchList: async ({ context }: SelectorQueryArgs) => { + const body = JSON.stringify({ apiKey: context.apiKey }) + const data = await fetchJson<{ items: { id: string; name: string }[] }>( + '/api/tools/monday/boards', + { method: 'POST', body } + ) + return (data.items || []).map((board) => ({ + id: board.id, + label: board.name, + })) + }, + }, + 'monday.columns': { + key: 'monday.columns', + staleTime: SELECTOR_STALE, + getQueryKey: ({ context }: SelectorQueryArgs) => [ + 'selectors', + 'monday.columns', + context.apiKey ?? 'none', + context.boardId ?? 'none', + ], + enabled: ({ context }) => Boolean(context.apiKey && context.boardId), + fetchList: async ({ context }: SelectorQueryArgs) => { + const body = JSON.stringify({ + apiKey: context.apiKey, + boardId: context.boardId, + }) + const data = await fetchJson<{ items: { id: string; name: string; type: string }[] }>( + '/api/tools/monday/columns', + { method: 'POST', body } + ) + return (data.items || []).map((col) => ({ + id: col.id, + label: `${col.name} (${col.type})`, + })) + }, + }, + 'monday.groups': { + key: 'monday.groups', + staleTime: SELECTOR_STALE, + getQueryKey: ({ context }: SelectorQueryArgs) => [ + 'selectors', + 'monday.groups', + context.apiKey ?? 'none', + context.boardId ?? 'none', + ], + enabled: ({ context }) => Boolean(context.apiKey && context.boardId), + fetchList: async ({ context }: SelectorQueryArgs) => { + const body = JSON.stringify({ + apiKey: context.apiKey, + boardId: context.boardId, + }) + const data = await fetchJson<{ items: { id: string; name: string }[] }>( + '/api/tools/monday/groups', + { method: 'POST', body } + ) + return (data.items || []).map((group) => ({ + id: group.id, + label: group.name, + })) + }, + }, + 'monday.status-options': { + key: 'monday.status-options', + staleTime: SELECTOR_STALE, + getQueryKey: ({ context }: SelectorQueryArgs) => [ + 'selectors', + 'monday.status-options', + context.apiKey ?? 'none', + context.boardId ?? 'none', + context.columnId ?? 'none', + ], + enabled: ({ context }) => Boolean(context.apiKey && context.boardId && context.columnId), + fetchList: async ({ context }: SelectorQueryArgs) => { + const body = JSON.stringify({ + apiKey: context.apiKey, + boardId: context.boardId, + columnId: context.columnId, + }) + const data = await fetchJson<{ items: { id: string; name: string }[] }>( + '/api/tools/monday/status-options', + { method: 'POST', body } + ) + return (data.items || []).map((option) => ({ + id: option.id, + label: option.name, + })) + }, + }, } export function getSelectorDefinition(key: SelectorKey): SelectorDefinition { diff --git a/apps/sim/hooks/selectors/resolution.ts b/apps/sim/hooks/selectors/resolution.ts index 78af03f935..5f5ea99bff 100644 --- a/apps/sim/hooks/selectors/resolution.ts +++ b/apps/sim/hooks/selectors/resolution.ts @@ -17,6 +17,9 @@ export interface SelectorResolutionArgs { knowledgeBaseId?: string siteId?: string collectionId?: string + apiKey?: string + boardId?: string + columnId?: string } const defaultContext: SelectorContext = {} @@ -58,6 +61,9 @@ function buildBaseContext( knowledgeBaseId: args.knowledgeBaseId, siteId: args.siteId, collectionId: args.collectionId, + apiKey: args.apiKey, + boardId: args.boardId, + columnId: args.columnId, ...extra, } } @@ -120,6 +126,20 @@ function resolveFileSelector( return { key: 'webflow.items', context, allowSearch: true } } return { key: null, context, allowSearch: true } + case 'monday': + if (subBlock.id === 'board_id' || subBlock.id === 'boardId') { + return { key: 'monday.boards', context, allowSearch: true } + } + if (subBlock.id === 'column_id' || subBlock.id === 'columnId') { + return { key: 'monday.columns', context, allowSearch: true } + } + if (subBlock.id === 'group_id' || subBlock.id === 'groupId') { + return { key: 'monday.groups', context, allowSearch: true } + } + if (subBlock.id === 'status_column' || subBlock.id === 'statusColumn') { + return { key: 'monday.status-options', context, allowSearch: true } + } + return { key: null, context, allowSearch: true } default: return { key: null, context, allowSearch: true } } diff --git a/apps/sim/hooks/selectors/types.ts b/apps/sim/hooks/selectors/types.ts index e9da5996a2..74a9fcbb7b 100644 --- a/apps/sim/hooks/selectors/types.ts +++ b/apps/sim/hooks/selectors/types.ts @@ -27,6 +27,11 @@ export type SelectorKey = | 'webflow.sites' | 'webflow.collections' | 'webflow.items' + | 'monday.boards' + | 'monday.columns' + | 'monday.groups' + | 'monday.status-options' + export interface SelectorOption { id: string @@ -49,6 +54,9 @@ export interface SelectorContext { fileId?: string siteId?: string collectionId?: string + apiKey?: string + boardId?: string + columnId?: string } export interface SelectorQueryArgs { diff --git a/apps/sim/lib/workflows/executor/execution-core.ts b/apps/sim/lib/workflows/executor/execution-core.ts index 403d690d76..b6df1950d6 100644 --- a/apps/sim/lib/workflows/executor/execution-core.ts +++ b/apps/sim/lib/workflows/executor/execution-core.ts @@ -148,6 +148,20 @@ export async function executeWorkflowCore( loops = draftData.loops parallels = draftData.parallels + // Debug: Log Monday blocks loaded from database + Object.entries(blocks).forEach(([blockId, block]) => { + if (block.type === 'monday') { + logger.info(`[${requestId}] Monday block loaded from database`, { + blockId, + subBlockKeys: Object.keys(block.subBlocks || {}), + subBlockValues: Object.entries(block.subBlocks || {}).reduce((acc, [key, sb]) => { + acc[key] = (sb as any).value + return acc + }, {} as Record), + }) + } + }) + logger.info( `[${requestId}] Using draft workflow state from normalized tables (client execution)` ) diff --git a/apps/sim/serializer/index.ts b/apps/sim/serializer/index.ts index bf996579fe..ebfbafef7a 100644 --- a/apps/sim/serializer/index.ts +++ b/apps/sim/serializer/index.ts @@ -479,12 +479,26 @@ export class Serializer { chosen = undefined } + // Preserve existing canonical key value if consolidation produces no better value + const existingCanonicalValue = params[canonicalKey] + const hasValidExistingValue = + existingCanonicalValue !== undefined && + existingCanonicalValue !== null && + (typeof existingCanonicalValue !== 'string' || existingCanonicalValue.trim().length > 0) + const sourceIds = [basicId, ...advancedIds].filter(Boolean) as string[] sourceIds.forEach((id) => { if (id !== canonicalKey) delete params[id] }) - if (chosen !== undefined) params[canonicalKey] = chosen - else delete params[canonicalKey] + + if (chosen !== undefined) { + params[canonicalKey] = chosen + } else if (hasValidExistingValue) { + // Keep existing value if consolidation produces nothing + params[canonicalKey] = existingCanonicalValue + } else { + delete params[canonicalKey] + } }) return params diff --git a/apps/sim/tools/monday/create_item.ts b/apps/sim/tools/monday/create_item.ts new file mode 100644 index 0000000000..cc2e9b7b65 --- /dev/null +++ b/apps/sim/tools/monday/create_item.ts @@ -0,0 +1,114 @@ +import type { CreateItemParams, CreateItemResponse } from '@/tools/monday/types' +import type { ToolConfig } from '@/tools/types' +import { createLogger } from '@sim/logger' +import { QUERIES } from './graphql' + +const logger = createLogger('MondayCreateItem') + +export const createItemTool: ToolConfig = { + id: 'monday_create_item', + name: 'Create Monday.com Item', + description: 'Create a new item in a Monday.com board', + version: '1.0.0', + + params: { + apiKey: { + type: 'string', + required: true, + visibility: 'hidden', + description: 'Monday.com API key', + }, + board_id: { + type: 'string', + required: true, + visibility: 'user-or-llm', + description: 'The ID of the board to create the item in', + }, + group_id: { + type: 'string', + required: false, + visibility: 'user-or-llm', + description: 'The group (section) ID within the board (optional)', + }, + item_name: { + type: 'string', + required: true, + visibility: 'user-or-llm', + description: 'The name of the item to create', + }, + column_values: { + type: 'json', + required: false, + visibility: 'user-or-llm', + description: 'Column values as JSON object (optional)', + }, + }, + + request: { + url: 'https://api.monday.com/v2', + method: 'POST', + headers: (params) => ({ + 'Content-Type': 'application/json', + Authorization: params.apiKey, + 'API-Version': '2024-01', + }), + body: (params) => ({ + query: QUERIES.CREATE_ITEM, + variables: { + boardId: parseInt(params.board_id, 10), + groupId: params.group_id, + itemName: params.item_name, + columnValues: params.column_values ? JSON.stringify(params.column_values) : undefined, + }, + }), + }, + + transformResponse: async (response: Response): Promise => { + if (!response.ok) { + const errorText = await response.text() + logger.error('Monday create item failed', { + status: response.status, + error: errorText, + }) + return { + success: false, + output: {}, + error: `Monday.com API error: ${response.status} - ${errorText}`, + } + } + + const result = await response.json() + + if (result.errors) { + logger.error('Monday GraphQL errors', { errors: result.errors }) + return { + success: false, + output: {}, + error: `GraphQL errors: ${JSON.stringify(result.errors)}`, + } + } + + const item = result.data?.create_item + + if (!item) { + return { + success: false, + output: {}, + error: 'No item returned from Monday.com', + } + } + + logger.info('Monday item created successfully', { + itemId: item.id, + boardId: item.board?.id, + }) + + return { + success: true, + output: { + item, + item_id: item.id, + }, + } + }, +} diff --git a/apps/sim/tools/monday/get_item.ts b/apps/sim/tools/monday/get_item.ts new file mode 100644 index 0000000000..c40fefc518 --- /dev/null +++ b/apps/sim/tools/monday/get_item.ts @@ -0,0 +1,87 @@ +import type { GetItemParams, GetItemResponse } from '@/tools/monday/types' +import type { ToolConfig } from '@/tools/types' +import { createLogger } from '@sim/logger' +import { QUERIES } from './graphql' + +const logger = createLogger('MondayGetItem') + +export const getItemTool: ToolConfig = { + id: 'monday_get_item', + name: 'Get Monday.com Item', + description: 'Retrieve a Monday.com item by ID', + version: '1.0.0', + + params: { + apiKey: { + type: 'string', + required: true, + visibility: 'hidden', + description: 'Monday.com API key', + }, + item_id: { + type: 'string', + required: true, + visibility: 'user-or-llm', + description: 'The ID of the item to retrieve', + }, + }, + + request: { + url: 'https://api.monday.com/v2', + method: 'POST', + headers: (params) => ({ + 'Content-Type': 'application/json', + Authorization: params.apiKey, + 'API-Version': '2024-01', + }), + body: (params) => ({ + query: QUERIES.GET_ITEM, + variables: { + itemId: [parseInt(params.item_id, 10)], + }, + }), + }, + + transformResponse: async (response: Response): Promise => { + if (!response.ok) { + const errorText = await response.text() + logger.error('Monday get item failed', { + status: response.status, + error: errorText, + }) + return { + success: false, + output: {}, + error: `Monday.com API error: ${response.status} - ${errorText}`, + } + } + + const result = await response.json() + + if (result.errors) { + logger.error('Monday GraphQL errors', { errors: result.errors }) + return { + success: false, + output: {}, + error: `GraphQL errors: ${JSON.stringify(result.errors)}`, + } + } + + const item = result.data?.items?.[0] + + if (!item) { + return { + success: false, + output: {}, + error: 'Item not found', + } + } + + logger.info('Monday item retrieved successfully', { itemId: item.id }) + + return { + success: true, + output: { item }, + } + }, +} diff --git a/apps/sim/tools/monday/graphql.ts b/apps/sim/tools/monday/graphql.ts new file mode 100644 index 0000000000..afe589d523 --- /dev/null +++ b/apps/sim/tools/monday/graphql.ts @@ -0,0 +1,210 @@ +import { createLogger } from '@sim/logger' + +const logger = createLogger('MondayGraphQL') + +/** + * GraphQL request structure + */ +export interface GraphQLRequest { + query: string + variables?: Record +} + +/** + * Execute a GraphQL query against the Monday.com API + */ +export async function executeMondayQuery( + apiKey: string, + request: GraphQLRequest +): Promise { + const response = await fetch('https://api.monday.com/v2', { + method: 'POST', + headers: { + 'Content-Type': 'application/json', + Authorization: apiKey, + 'API-Version': '2024-01', + }, + body: JSON.stringify(request), + }) + + if (!response.ok) { + const errorText = await response.text() + logger.error('Monday.com API error', { + status: response.status, + error: errorText, + }) + throw new Error(`Monday.com API error: ${response.status} - ${errorText}`) + } + + const data = await response.json() + + if (data.errors) { + logger.error('Monday.com GraphQL errors', { errors: data.errors }) + throw new Error(`GraphQL errors: ${JSON.stringify(data.errors)}`) + } + + return data.data as T +} + +/** + * Common GraphQL queries for Monday.com + */ +export const QUERIES = { + GET_BOARDS: ` + query { + boards { + id + name + description + board_kind + state + } + } + `, + + GET_BOARD_COLUMNS: ` + query ($boardId: [ID!]!) { + boards(ids: $boardId) { + columns { + id + title + type + settings_str + } + } + } + `, + + GET_BOARD_GROUPS: ` + query ($boardId: [ID!]!) { + boards(ids: $boardId) { + groups { + id + title + color + } + } + } + `, + + CREATE_ITEM: ` + mutation ($boardId: ID!, $groupId: String, $itemName: String!, $columnValues: JSON) { + create_item( + board_id: $boardId + group_id: $groupId + item_name: $itemName + column_values: $columnValues + ) { + id + name + created_at + board { id } + group { id } + column_values { + id + type + text + value + } + } + } + `, + + UPDATE_ITEM: ` + mutation ($boardId: ID, $itemId: ID!, $columnValues: JSON!) { + change_multiple_column_values( + board_id: $boardId + item_id: $itemId + column_values: $columnValues + ) { + id + name + updated_at + column_values { + id + type + text + value + } + } + } + `, + + GET_ITEM: ` + query ($itemId: [ID!]!) { + items(ids: $itemId) { + id + name + created_at + updated_at + board { id } + group { id } + column_values { + id + type + text + value + } + } + } + `, + + LIST_ITEMS: ` + query ($boardId: [ID!]!, $limit: Int, $groupId: String) { + boards(ids: $boardId) { + items_page(limit: $limit, query_params: {rules: [{column_id: "group", compare_value: [$groupId]}]}) { + items { + id + name + created_at + updated_at + board { id } + group { id } + column_values { + id + type + text + value + } + } + } + } + } + `, + + LIST_ITEMS_NO_FILTER: ` + query ($boardId: [ID!]!, $limit: Int) { + boards(ids: $boardId) { + items_page(limit: $limit) { + items { + id + name + created_at + updated_at + board { id } + group { id } + column_values { + id + type + text + value + } + } + } + } + } + `, + + GET_COLUMN_SETTINGS: ` + query ($boardId: [ID!]!, $columnId: String!) { + boards(ids: $boardId) { + columns(ids: [$columnId]) { + id + title + type + settings_str + } + } + } + `, +} diff --git a/apps/sim/tools/monday/index.ts b/apps/sim/tools/monday/index.ts new file mode 100644 index 0000000000..c005cb6e73 --- /dev/null +++ b/apps/sim/tools/monday/index.ts @@ -0,0 +1,4 @@ +export { createItemTool as mondayCreateItemTool } from './create_item' +export { updateItemTool as mondayUpdateItemTool } from './update_item' +export { getItemTool as mondayGetItemTool } from './get_item' +export { listItemsTool as mondayListItemsTool } from './list_items' diff --git a/apps/sim/tools/monday/list_items.ts b/apps/sim/tools/monday/list_items.ts new file mode 100644 index 0000000000..e833f9a739 --- /dev/null +++ b/apps/sim/tools/monday/list_items.ts @@ -0,0 +1,96 @@ +import type { ListItemsParams, ListItemsResponse } from '@/tools/monday/types' +import type { ToolConfig } from '@/tools/types' +import { createLogger } from '@sim/logger' +import { QUERIES } from './graphql' + +const logger = createLogger('MondayListItems') + +export const listItemsTool: ToolConfig = { + id: 'monday_list_items', + name: 'List Monday.com Items', + description: 'List items from a Monday.com board', + version: '1.0.0', + + params: { + apiKey: { + type: 'string', + required: true, + visibility: 'hidden', + description: 'Monday.com API key', + }, + board_id: { + type: 'string', + required: true, + visibility: 'user-or-llm', + description: 'The ID of the board to list items from', + }, + group_id: { + type: 'string', + required: false, + visibility: 'user-or-llm', + description: 'Filter by group ID (optional)', + }, + limit: { + type: 'number', + required: false, + visibility: 'user-or-llm', + description: 'Maximum number of items to return (default: 25)', + }, + }, + + request: { + url: 'https://api.monday.com/v2', + method: 'POST', + headers: (params) => ({ + 'Content-Type': 'application/json', + Authorization: params.apiKey, + 'API-Version': '2024-01', + }), + body: (params) => { + const query = params.group_id ? QUERIES.LIST_ITEMS : QUERIES.LIST_ITEMS_NO_FILTER + return { + query, + variables: { + boardId: [parseInt(params.board_id, 10)], + limit: params.limit || 25, + groupId: params.group_id, + }, + } + }, + }, + + transformResponse: async (response: Response): Promise => { + if (!response.ok) { + const errorText = await response.text() + logger.error('Monday list items failed', { + status: response.status, + error: errorText, + }) + return { + success: false, + output: {}, + error: `Monday.com API error: ${response.status} - ${errorText}`, + } + } + + const result = await response.json() + + if (result.errors) { + logger.error('Monday GraphQL errors', { errors: result.errors }) + return { + success: false, + output: {}, + error: `GraphQL errors: ${JSON.stringify(result.errors)}`, + } + } + + const items = result.data?.boards?.[0]?.items_page?.items || [] + + logger.info('Monday items listed successfully', { count: items.length }) + + return { + success: true, + output: { items }, + } + }, +} diff --git a/apps/sim/tools/monday/types.ts b/apps/sim/tools/monday/types.ts new file mode 100644 index 0000000000..b8a057bf75 --- /dev/null +++ b/apps/sim/tools/monday/types.ts @@ -0,0 +1,145 @@ +import type { ToolResponse } from '@/tools/types' + +/** + * Monday.com Board + */ +export interface MondayBoard { + id: string + name: string + description?: string + board_kind: string + state: string +} + +/** + * Monday.com Column + */ +export interface MondayColumn { + id: string + title: string + type: string + settings_str?: string +} + +/** + * Monday.com Group + */ +export interface MondayGroup { + id: string + title: string + color: string +} + +/** + * Monday.com Column Value + */ +export interface MondayColumnValue { + id: string + title: string + type: string + text?: string + value?: string +} + +/** + * Monday.com Item + */ +export interface MondayItem { + id: string + name: string + board: { id: string } + group: { id: string } + column_values: MondayColumnValue[] + created_at: string + updated_at: string +} + +/** + * Parameters for creating a Monday.com item + */ +export interface CreateItemParams { + apiKey: string + board_id: string + group_id?: string + item_name: string + column_values?: Record +} + +/** + * Response from creating a Monday.com item + */ +export interface CreateItemResponse extends ToolResponse { + output: { + item?: MondayItem + item_id?: string + } +} + +/** + * Parameters for updating a Monday.com item + */ +export interface UpdateItemParams { + apiKey: string + item_id: string + board_id?: string + column_values: Record +} + +/** + * Response from updating a Monday.com item + */ +export interface UpdateItemResponse extends ToolResponse { + output: { + item?: MondayItem + item_id?: string + } +} + +/** + * Parameters for getting a Monday.com item + */ +export interface GetItemParams { + apiKey: string + item_id: string +} + +/** + * Response from getting a Monday.com item + */ +export interface GetItemResponse extends ToolResponse { + output: { + item?: MondayItem + } +} + +/** + * Parameters for listing Monday.com items + */ +export interface ListItemsParams { + apiKey: string + board_id: string + group_id?: string + limit?: number +} + +/** + * Response from listing Monday.com items + */ +export interface ListItemsResponse extends ToolResponse { + output: { + items?: MondayItem[] + } +} + +/** + * Generic Monday.com response type for blocks + */ +export type MondayResponse = { + success: boolean + output: { + item?: MondayItem + items?: MondayItem[] + item_id?: string + } + error?: string +} diff --git a/apps/sim/tools/monday/update_item.ts b/apps/sim/tools/monday/update_item.ts new file mode 100644 index 0000000000..968ccedbdb --- /dev/null +++ b/apps/sim/tools/monday/update_item.ts @@ -0,0 +1,104 @@ +import type { UpdateItemParams, UpdateItemResponse } from '@/tools/monday/types' +import type { ToolConfig } from '@/tools/types' +import { createLogger } from '@sim/logger' +import { QUERIES } from './graphql' + +const logger = createLogger('MondayUpdateItem') + +export const updateItemTool: ToolConfig = { + id: 'monday_update_item', + name: 'Update Monday.com Item', + description: 'Update column values in an existing Monday.com item', + version: '1.0.0', + + params: { + apiKey: { + type: 'string', + required: true, + visibility: 'hidden', + description: 'Monday.com API key', + }, + item_id: { + type: 'string', + required: true, + visibility: 'user-or-llm', + description: 'The ID of the item to update', + }, + board_id: { + type: 'string', + required: false, + visibility: 'user-or-llm', + description: 'The board ID (optional but recommended)', + }, + column_values: { + type: 'json', + required: true, + visibility: 'user-or-llm', + description: 'Column values to update as JSON object', + }, + }, + + request: { + url: 'https://api.monday.com/v2', + method: 'POST', + headers: (params) => ({ + 'Content-Type': 'application/json', + Authorization: params.apiKey, + 'API-Version': '2024-01', + }), + body: (params) => ({ + query: QUERIES.UPDATE_ITEM, + variables: { + boardId: params.board_id ? parseInt(params.board_id, 10) : undefined, + itemId: parseInt(params.item_id, 10), + columnValues: JSON.stringify(params.column_values), + }, + }), + }, + + transformResponse: async (response: Response): Promise => { + if (!response.ok) { + const errorText = await response.text() + logger.error('Monday update item failed', { + status: response.status, + error: errorText, + }) + return { + success: false, + output: {}, + error: `Monday.com API error: ${response.status} - ${errorText}`, + } + } + + const result = await response.json() + + if (result.errors) { + logger.error('Monday GraphQL errors', { errors: result.errors }) + return { + success: false, + output: {}, + error: `GraphQL errors: ${JSON.stringify(result.errors)}`, + } + } + + const item = result.data?.change_multiple_column_values + + if (!item) { + return { + success: false, + output: {}, + error: 'No item returned from Monday.com', + } + } + + logger.info('Monday item updated successfully', { itemId: item.id }) + + return { + success: true, + output: { + item, + item_id: item.id, + }, + } + }, +} diff --git a/apps/sim/tools/registry.ts b/apps/sim/tools/registry.ts index 5d28bc397b..15e12cdf0a 100644 --- a/apps/sim/tools/registry.ts +++ b/apps/sim/tools/registry.ts @@ -593,6 +593,12 @@ import { import { linkedInGetProfileTool, linkedInSharePostTool } from '@/tools/linkedin' import { linkupSearchTool } from '@/tools/linkup' import { llmChatTool } from '@/tools/llm' +import { + mondayCreateItemTool, + mondayUpdateItemTool, + mondayGetItemTool, + mondayListItemsTool, +} from '@/tools/monday' import { mailchimpAddMemberTagsTool, mailchimpAddMemberTool, @@ -1424,6 +1430,10 @@ export const tools: Record = { linkup_search: linkupSearchTool, linkedin_share_post: linkedInSharePostTool, linkedin_get_profile: linkedInGetProfileTool, + monday_create_item: mondayCreateItemTool, + monday_update_item: mondayUpdateItemTool, + monday_get_item: mondayGetItemTool, + monday_list_items: mondayListItemsTool, resend_send: mailSendTool, sendgrid_send_mail: sendGridSendMailTool, sendgrid_add_contact: sendGridAddContactTool, diff --git a/apps/sim/triggers/monday/column_changed.ts b/apps/sim/triggers/monday/column_changed.ts new file mode 100644 index 0000000000..37242ede9a --- /dev/null +++ b/apps/sim/triggers/monday/column_changed.ts @@ -0,0 +1,129 @@ +import { MondayIcon } from '@/components/icons' +import { createLogger } from '@sim/logger' +import type { TriggerConfig } from '@/triggers/types' + +const logger = createLogger('MondayColumnChangedTrigger') + +export const mondayColumnChangedTrigger: TriggerConfig = { + id: 'monday_column_changed', + name: 'Monday.com Column Changed', + provider: 'monday', + description: 'Triggers when a specific column value changes in a Monday.com board', + version: '1.0.0', + icon: MondayIcon, + + subBlocks: [ + { + id: 'apiKey', + title: 'API Key', + type: 'short-input', + description: 'Your Monday.com API key (get it from Admin > API section)', + placeholder: 'Enter API key', + password: true, + required: true, + mode: 'trigger', + }, + { + id: 'boardId', + title: 'Board to Monitor', + type: 'file-selector', + serviceId: 'monday', + placeholder: 'Select a Monday.com board', + description: 'The board to monitor for column changes', + required: true, + dependsOn: ['apiKey'], + mode: 'trigger', + }, + { + id: 'columnId', + title: 'Column to Monitor', + type: 'file-selector', + serviceId: 'monday', + placeholder: 'Select a column', + description: 'The specific column to monitor for changes', + required: true, + dependsOn: ['apiKey', 'boardId'], + mode: 'trigger', + }, + { + id: 'specificValue', + title: 'Specific Value (Optional)', + type: 'short-input', + placeholder: 'e.g., "Done" or "Working on it"', + description: 'Only trigger when column changes to this specific value (optional)', + required: false, + mode: 'trigger', + }, + { + id: 'pollingInterval', + title: 'Polling Interval', + type: 'dropdown', + options: [ + { label: 'Every 5 minutes', id: '5' }, + { label: 'Every 15 minutes', id: '15' }, + { label: 'Every 30 minutes', id: '30' }, + { label: 'Every hour', id: '60' }, + ], + defaultValue: '15', + description: 'How often to check for column changes', + required: true, + mode: 'trigger', + }, + { + id: 'triggerInstructions', + title: 'Setup Instructions', + hideFromPreview: true, + type: 'text', + defaultValue: [ + 'Get your Monday.com API key from Settings > Admin > API', + 'Select the board and column you want to monitor', + 'Optionally specify a specific value to trigger on', + 'The trigger will detect changes at the specified interval', + 'Each change will include both old and new values', + ] + .map((instruction, index) => `${index + 1}. ${instruction}`) + .join('\n'), + mode: 'trigger', + }, + { + id: 'triggerSave', + title: '', + type: 'trigger-save', + hideFromPreview: true, + mode: 'trigger', + triggerId: 'monday_column_changed', + }, + ], + + outputs: { + item: { + id: { type: 'string', description: 'Item ID' }, + name: { type: 'string', description: 'Item name' }, + board_id: { type: 'string', description: 'Board ID' }, + group_id: { type: 'string', description: 'Group ID' }, + column_values: { type: 'json', description: 'All column values' }, + created_at: { type: 'string', description: 'Creation timestamp' }, + updated_at: { type: 'string', description: 'Last update timestamp' }, + }, + old_value: { + type: 'string', + description: 'Previous column value', + }, + new_value: { + type: 'string', + description: 'New column value', + }, + column_id: { + type: 'string', + description: 'ID of the changed column', + }, + column_title: { + type: 'string', + description: 'Title of the changed column', + }, + timestamp: { + type: 'string', + description: 'Trigger timestamp', + }, + }, +} diff --git a/apps/sim/triggers/monday/index.ts b/apps/sim/triggers/monday/index.ts new file mode 100644 index 0000000000..89c1571a9b --- /dev/null +++ b/apps/sim/triggers/monday/index.ts @@ -0,0 +1,2 @@ +export { mondayNewItemTrigger } from './new_item' +export { mondayColumnChangedTrigger } from './column_changed' diff --git a/apps/sim/triggers/monday/new_item.ts b/apps/sim/triggers/monday/new_item.ts new file mode 100644 index 0000000000..581ca5ebf3 --- /dev/null +++ b/apps/sim/triggers/monday/new_item.ts @@ -0,0 +1,104 @@ +import { MondayIcon } from '@/components/icons' +import { createLogger } from '@sim/logger' +import type { TriggerConfig } from '@/triggers/types' + +const logger = createLogger('MondayNewItemTrigger') + +export const mondayNewItemTrigger: TriggerConfig = { + id: 'monday_new_item', + name: 'Monday.com New Item', + provider: 'monday', + description: 'Triggers when a new item is added to a Monday.com board', + version: '1.0.0', + icon: MondayIcon, + + subBlocks: [ + { + id: 'apiKey', + title: 'API Key', + type: 'short-input', + description: 'Your Monday.com API key (get it from Admin > API section)', + placeholder: 'Enter API key', + password: true, + required: true, + mode: 'trigger', + }, + { + id: 'boardId', + title: 'Board to Monitor', + type: 'file-selector', + serviceId: 'monday', + placeholder: 'Select a Monday.com board', + description: 'The board to monitor for new items', + required: true, + dependsOn: ['apiKey'], + mode: 'trigger', + }, + { + id: 'groupId', + title: 'Group Filter (Optional)', + type: 'file-selector', + serviceId: 'monday', + placeholder: 'All groups', + description: 'Filter by specific group (optional)', + required: false, + dependsOn: ['apiKey', 'boardId'], + mode: 'trigger', + }, + { + id: 'pollingInterval', + title: 'Polling Interval', + type: 'dropdown', + options: [ + { label: 'Every 5 minutes', id: '5' }, + { label: 'Every 15 minutes', id: '15' }, + { label: 'Every 30 minutes', id: '30' }, + { label: 'Every hour', id: '60' }, + ], + defaultValue: '15', + description: 'How often to check for new items', + required: true, + mode: 'trigger', + }, + { + id: 'triggerInstructions', + title: 'Setup Instructions', + hideFromPreview: true, + type: 'text', + defaultValue: [ + 'Get your Monday.com API key from Settings > Admin > API', + 'Select the board you want to monitor', + 'Optionally filter by a specific group', + 'The trigger will check for new items at the specified interval', + 'New items will be detected based on their creation time', + ] + .map((instruction, index) => `${index + 1}. ${instruction}`) + .join('\n'), + mode: 'trigger', + }, + { + id: 'triggerSave', + title: '', + type: 'trigger-save', + hideFromPreview: true, + mode: 'trigger', + triggerId: 'monday_new_item', + }, + ], + + outputs: { + item: { + id: { type: 'string', description: 'Item ID' }, + name: { type: 'string', description: 'Item name' }, + board_id: { type: 'string', description: 'Board ID' }, + group_id: { type: 'string', description: 'Group ID' }, + column_values: { type: 'json', description: 'All column values' }, + created_at: { type: 'string', description: 'Creation timestamp' }, + updated_at: { type: 'string', description: 'Last update timestamp' }, + }, + timestamp: { + type: 'string', + description: 'Trigger timestamp', + }, + }, +} diff --git a/apps/sim/triggers/registry.ts b/apps/sim/triggers/registry.ts index 942ff0e018..60c2b21939 100644 --- a/apps/sim/triggers/registry.ts +++ b/apps/sim/triggers/registry.ts @@ -84,6 +84,7 @@ import { microsoftTeamsChatSubscriptionTrigger, microsoftTeamsWebhookTrigger, } from '@/triggers/microsoftteams' +import { mondayNewItemTrigger, mondayColumnChangedTrigger } from '@/triggers/monday' import { outlookPollingTrigger } from '@/triggers/outlook' import { rssPollingTrigger } from '@/triggers/rss' import { slackWebhookTrigger } from '@/triggers/slack' @@ -153,6 +154,8 @@ export const TRIGGER_REGISTRY: TriggerRegistry = { linear_customer_request_updated: linearCustomerRequestUpdatedTrigger, microsoftteams_webhook: microsoftTeamsWebhookTrigger, microsoftteams_chat_subscription: microsoftTeamsChatSubscriptionTrigger, + monday_new_item: mondayNewItemTrigger, + monday_column_changed: mondayColumnChangedTrigger, outlook_poller: outlookPollingTrigger, rss_poller: rssPollingTrigger, stripe_webhook: stripeWebhookTrigger, diff --git a/bun.lock b/bun.lock index e548d025bd..917609c7d1 100644 --- a/bun.lock +++ b/bun.lock @@ -1,6 +1,5 @@ { "lockfileVersion": 1, - "configVersion": 0, "workspaces": { "": { "name": "simstudio", From 239371f5590f469a08d85856afde13aa900db07e Mon Sep 17 00:00:00 2001 From: shubhamxshah Date: Tue, 30 Dec 2025 16:53:35 +0530 Subject: [PATCH 03/10] all fields work --- .../file-selector/file-selector-input.tsx | 52 ++++++++++++++++++- .../file-selector/file-selector-input.tsx | 25 ++++++++- apps/sim/blocks/blocks/monday.ts | 10 ++-- apps/sim/hooks/selectors/resolution.ts | 13 ++++- apps/sim/hooks/selectors/types.ts | 1 + apps/sim/serializer/index.ts | 19 ++++--- apps/sim/tools/monday/graphql.ts | 6 +-- apps/sim/tools/monday/list_items.ts | 15 +++--- apps/sim/tools/monday/update_item.ts | 6 +-- 9 files changed, 121 insertions(+), 26 deletions(-) diff --git a/apps/sim/app/workspace/[workspaceId]/w/[workflowId]/components/panel/components/editor/components/sub-block/components/file-selector/file-selector-input.tsx b/apps/sim/app/workspace/[workspaceId]/w/[workflowId]/components/panel/components/editor/components/sub-block/components/file-selector/file-selector-input.tsx index 27415b31a4..01ff549a7f 100644 --- a/apps/sim/app/workspace/[workspaceId]/w/[workflowId]/components/panel/components/editor/components/sub-block/components/file-selector/file-selector-input.tsx +++ b/apps/sim/app/workspace/[workspaceId]/w/[workflowId]/components/panel/components/editor/components/sub-block/components/file-selector/file-selector-input.tsx @@ -49,6 +49,16 @@ export function FileSelectorInput({ const [teamIdValueFromStore] = useSubBlockValue(blockId, 'teamId') const [siteIdValueFromStore] = useSubBlockValue(blockId, 'siteId') const [collectionIdValueFromStore] = useSubBlockValue(blockId, 'collectionId') + const [apiKeyValueFromStore] = useSubBlockValue(blockId, 'apiKey') + const [boardIdValueFromStore] = useSubBlockValue(blockId, 'board_id') + const [boardIdCamelFromStore] = useSubBlockValue(blockId, 'boardId') + const [boardIdListFromStore] = useSubBlockValue(blockId, 'board_id_list') + const [boardIdUpdateFromStore] = useSubBlockValue(blockId, 'board_id_update') + const [groupIdValueFromStore] = useSubBlockValue(blockId, 'group_id') + const [groupIdCamelFromStore] = useSubBlockValue(blockId, 'groupId') + const [groupIdListFromStore] = useSubBlockValue(blockId, 'group_id_list') + const [columnIdValueFromStore] = useSubBlockValue(blockId, 'column_id') + const [columnIdCamelFromStore] = useSubBlockValue(blockId, 'columnId') const connectedCredential = previewContextValues?.credential ?? connectedCredentialFromStore const domainValue = previewContextValues?.domain ?? domainValueFromStore @@ -57,6 +67,25 @@ export function FileSelectorInput({ const teamIdValue = previewContextValues?.teamId ?? teamIdValueFromStore const siteIdValue = previewContextValues?.siteId ?? siteIdValueFromStore const collectionIdValue = previewContextValues?.collectionId ?? collectionIdValueFromStore + const apiKeyValue = previewContextValues?.apiKey ?? apiKeyValueFromStore + const boardIdValue = + previewContextValues?.board_id ?? + previewContextValues?.boardId ?? + boardIdValueFromStore ?? + boardIdCamelFromStore ?? + boardIdListFromStore ?? + boardIdUpdateFromStore + const groupIdValue = + previewContextValues?.group_id ?? + previewContextValues?.groupId ?? + groupIdValueFromStore ?? + groupIdCamelFromStore ?? + groupIdListFromStore + const columnIdValue = + previewContextValues?.column_id ?? + previewContextValues?.columnId ?? + columnIdValueFromStore ?? + columnIdCamelFromStore const normalizedCredentialId = typeof connectedCredential === 'string' @@ -81,6 +110,10 @@ export function FileSelectorInput({ teamId: (teamIdValue as string) || undefined, siteId: (siteIdValue as string) || undefined, collectionId: (collectionIdValue as string) || undefined, + apiKey: (apiKeyValue as string) || undefined, + boardId: (boardIdValue as string) || undefined, + groupId: (groupIdValue as string) || undefined, + columnId: (columnIdValue as string) || undefined, }) }, [ subBlock, @@ -92,9 +125,15 @@ export function FileSelectorInput({ teamIdValue, siteIdValue, collectionIdValue, + apiKeyValue, + boardIdValue, + groupIdValue, + columnIdValue, ]) - const missingCredential = !normalizedCredentialId + const isMondaySelector = selectorResolution?.key?.startsWith('monday.') + const missingCredential = !isMondaySelector && !normalizedCredentialId + const missingApiKey = isMondaySelector && !selectorResolution?.context.apiKey const missingDomain = selectorResolution?.key && (selectorResolution.key === 'confluence.pages' || selectorResolution.key === 'jira.issues') && @@ -109,16 +148,27 @@ export function FileSelectorInput({ selectorResolution?.key === 'webflow.collections' && !selectorResolution.context.siteId const missingCollection = selectorResolution?.key === 'webflow.items' && !selectorResolution.context.collectionId + const missingBoard = + isMondaySelector && + (selectorResolution?.key === 'monday.groups' || selectorResolution?.key === 'monday.columns') && + !selectorResolution?.context.boardId + const missingColumn = + isMondaySelector && + selectorResolution?.key === 'monday.status-options' && + !selectorResolution?.context.columnId const disabledReason = finalDisabled || isForeignCredential || missingCredential || + missingApiKey || missingDomain || missingProject || missingPlan || missingSite || missingCollection || + missingBoard || + missingColumn || !selectorResolution?.key if (!selectorResolution?.key) { diff --git a/apps/sim/app/workspace/[workspaceId]/w/[workflowId]/components/panel/components/sub-block/components/file-selector/file-selector-input.tsx b/apps/sim/app/workspace/[workspaceId]/w/[workflowId]/components/panel/components/sub-block/components/file-selector/file-selector-input.tsx index e7d101dc6b..6f664f600d 100644 --- a/apps/sim/app/workspace/[workspaceId]/w/[workflowId]/components/panel/components/sub-block/components/file-selector/file-selector-input.tsx +++ b/apps/sim/app/workspace/[workspaceId]/w/[workflowId]/components/panel/components/sub-block/components/file-selector/file-selector-input.tsx @@ -54,6 +54,11 @@ export function FileSelectorInput({ const [apiKeyValueFromStore] = useSubBlockValue(blockId, 'apiKey') const [boardIdValueFromStore] = useSubBlockValue(blockId, 'board_id') const [boardIdCamelFromStore] = useSubBlockValue(blockId, 'boardId') + const [boardIdListFromStore] = useSubBlockValue(blockId, 'board_id_list') + const [boardIdUpdateFromStore] = useSubBlockValue(blockId, 'board_id_update') + const [groupIdValueFromStore] = useSubBlockValue(blockId, 'group_id') + const [groupIdCamelFromStore] = useSubBlockValue(blockId, 'groupId') + const [groupIdListFromStore] = useSubBlockValue(blockId, 'group_id_list') const [columnIdValueFromStore] = useSubBlockValue(blockId, 'column_id') const [columnIdCamelFromStore] = useSubBlockValue(blockId, 'columnId') @@ -66,7 +71,18 @@ export function FileSelectorInput({ const collectionIdValue = previewContextValues?.collectionId ?? collectionIdValueFromStore const apiKeyValue = previewContextValues?.apiKey ?? apiKeyValueFromStore const boardIdValue = - previewContextValues?.board_id ?? previewContextValues?.boardId ?? boardIdValueFromStore ?? boardIdCamelFromStore + previewContextValues?.board_id ?? + previewContextValues?.boardId ?? + boardIdValueFromStore ?? + boardIdCamelFromStore ?? + boardIdListFromStore ?? + boardIdUpdateFromStore + const groupIdValue = + previewContextValues?.group_id ?? + previewContextValues?.groupId ?? + groupIdValueFromStore ?? + groupIdCamelFromStore ?? + groupIdListFromStore const columnIdValue = previewContextValues?.column_id ?? previewContextValues?.columnId ?? @@ -98,6 +114,7 @@ export function FileSelectorInput({ collectionId: (collectionIdValue as string) || undefined, apiKey: (apiKeyValue as string) || undefined, boardId: (boardIdValue as string) || undefined, + groupId: (groupIdValue as string) || undefined, columnId: (columnIdValue as string) || undefined, }) }, [ @@ -112,9 +129,15 @@ export function FileSelectorInput({ collectionIdValue, apiKeyValue, boardIdValue, + groupIdValue, columnIdValue, boardIdValueFromStore, boardIdCamelFromStore, + boardIdListFromStore, + boardIdUpdateFromStore, + groupIdValueFromStore, + groupIdCamelFromStore, + groupIdListFromStore, columnIdValueFromStore, columnIdCamelFromStore, ]) diff --git a/apps/sim/blocks/blocks/monday.ts b/apps/sim/blocks/blocks/monday.ts index efa6889d98..44df4c5906 100644 --- a/apps/sim/blocks/blocks/monday.ts +++ b/apps/sim/blocks/blocks/monday.ts @@ -85,12 +85,14 @@ export const MondayBlock: BlockConfig = { }, { id: 'board_id_update', - title: 'Board ID (Optional)', - type: 'short-input', + title: 'Board', + type: 'file-selector', + serviceId: 'monday', canonicalParamId: 'board_id', - placeholder: 'Enter board ID (recommended)', - required: false, + placeholder: 'Select a Monday.com board', + required: true, condition: { field: 'operation', value: 'monday_update_item' }, + dependsOn: ['apiKey'], }, { id: 'column_values_update', diff --git a/apps/sim/hooks/selectors/resolution.ts b/apps/sim/hooks/selectors/resolution.ts index 5f5ea99bff..f8bb92ca96 100644 --- a/apps/sim/hooks/selectors/resolution.ts +++ b/apps/sim/hooks/selectors/resolution.ts @@ -127,13 +127,22 @@ function resolveFileSelector( } return { key: null, context, allowSearch: true } case 'monday': - if (subBlock.id === 'board_id' || subBlock.id === 'boardId') { + if ( + subBlock.id === 'board_id' || + subBlock.id === 'boardId' || + subBlock.id === 'board_id_list' || + subBlock.id === 'board_id_update' + ) { return { key: 'monday.boards', context, allowSearch: true } } if (subBlock.id === 'column_id' || subBlock.id === 'columnId') { return { key: 'monday.columns', context, allowSearch: true } } - if (subBlock.id === 'group_id' || subBlock.id === 'groupId') { + if ( + subBlock.id === 'group_id' || + subBlock.id === 'groupId' || + subBlock.id === 'group_id_list' + ) { return { key: 'monday.groups', context, allowSearch: true } } if (subBlock.id === 'status_column' || subBlock.id === 'statusColumn') { diff --git a/apps/sim/hooks/selectors/types.ts b/apps/sim/hooks/selectors/types.ts index 74a9fcbb7b..01b5c5306d 100644 --- a/apps/sim/hooks/selectors/types.ts +++ b/apps/sim/hooks/selectors/types.ts @@ -56,6 +56,7 @@ export interface SelectorContext { collectionId?: string apiKey?: string boardId?: string + groupId?: string columnId?: string } diff --git a/apps/sim/serializer/index.ts b/apps/sim/serializer/index.ts index ebfbafef7a..6296959f53 100644 --- a/apps/sim/serializer/index.ts +++ b/apps/sim/serializer/index.ts @@ -449,19 +449,26 @@ export class Serializer { }) // Finally, consolidate canonical parameters (e.g., selector and manual ID into a single param) - const canonicalGroups: Record = {} + const canonicalGroups: Record = {} blockConfig.subBlocks.forEach((sb) => { if (!sb.canonicalParamId) return const key = sb.canonicalParamId - if (!canonicalGroups[key]) canonicalGroups[key] = { basic: undefined, advanced: [] } + if (!canonicalGroups[key]) canonicalGroups[key] = { basic: [], advanced: [] } if (sb.mode === 'advanced') canonicalGroups[key].advanced.push(sb.id) - else canonicalGroups[key].basic = sb.id + else canonicalGroups[key].basic.push(sb.id) }) Object.entries(canonicalGroups).forEach(([canonicalKey, group]) => { - const basicId = group.basic + const basicIds = group.basic const advancedIds = group.advanced - const basicVal = basicId ? params[basicId] : undefined + + // Check all basic field IDs for a value (not just the last one) + const basicVal = basicIds + .map((id) => params[id]) + .find( + (v) => v !== undefined && v !== null && (typeof v !== 'string' || v.trim().length > 0) + ) + const advancedVal = advancedIds .map((id) => params[id]) .find( @@ -486,7 +493,7 @@ export class Serializer { existingCanonicalValue !== null && (typeof existingCanonicalValue !== 'string' || existingCanonicalValue.trim().length > 0) - const sourceIds = [basicId, ...advancedIds].filter(Boolean) as string[] + const sourceIds = [...basicIds, ...advancedIds].filter(Boolean) as string[] sourceIds.forEach((id) => { if (id !== canonicalKey) delete params[id] }) diff --git a/apps/sim/tools/monday/graphql.ts b/apps/sim/tools/monday/graphql.ts index afe589d523..94f5d67d1c 100644 --- a/apps/sim/tools/monday/graphql.ts +++ b/apps/sim/tools/monday/graphql.ts @@ -111,7 +111,7 @@ export const QUERIES = { `, UPDATE_ITEM: ` - mutation ($boardId: ID, $itemId: ID!, $columnValues: JSON!) { + mutation ($boardId: ID!, $itemId: ID!, $columnValues: JSON!) { change_multiple_column_values( board_id: $boardId item_id: $itemId @@ -150,9 +150,9 @@ export const QUERIES = { `, LIST_ITEMS: ` - query ($boardId: [ID!]!, $limit: Int, $groupId: String) { + query ($boardId: [ID!]!, $limit: Int) { boards(ids: $boardId) { - items_page(limit: $limit, query_params: {rules: [{column_id: "group", compare_value: [$groupId]}]}) { + items_page(limit: $limit) { items { id name diff --git a/apps/sim/tools/monday/list_items.ts b/apps/sim/tools/monday/list_items.ts index e833f9a739..6ce4aaf788 100644 --- a/apps/sim/tools/monday/list_items.ts +++ b/apps/sim/tools/monday/list_items.ts @@ -47,19 +47,17 @@ export const listItemsTool: ToolConfig = { 'API-Version': '2024-01', }), body: (params) => { - const query = params.group_id ? QUERIES.LIST_ITEMS : QUERIES.LIST_ITEMS_NO_FILTER return { - query, + query: QUERIES.LIST_ITEMS, variables: { boardId: [parseInt(params.board_id, 10)], limit: params.limit || 25, - groupId: params.group_id, }, } }, }, - transformResponse: async (response: Response): Promise => { + transformResponse: async (response: Response, params): Promise => { if (!response.ok) { const errorText = await response.text() logger.error('Monday list items failed', { @@ -84,9 +82,14 @@ export const listItemsTool: ToolConfig = { } } - const items = result.data?.boards?.[0]?.items_page?.items || [] + let items = result.data?.boards?.[0]?.items_page?.items || [] - logger.info('Monday items listed successfully', { count: items.length }) + // Filter by group if group_id is provided + if (params.group_id) { + items = items.filter((item: any) => item.group?.id === params.group_id) + } + + logger.info('Monday items listed successfully', { count: items.length, filtered: !!params.group_id }) return { success: true, diff --git a/apps/sim/tools/monday/update_item.ts b/apps/sim/tools/monday/update_item.ts index 968ccedbdb..78923f108c 100644 --- a/apps/sim/tools/monday/update_item.ts +++ b/apps/sim/tools/monday/update_item.ts @@ -26,9 +26,9 @@ export const updateItemTool: ToolConfig = }, board_id: { type: 'string', - required: false, + required: true, visibility: 'user-or-llm', - description: 'The board ID (optional but recommended)', + description: 'The board ID containing the item', }, column_values: { type: 'json', @@ -49,7 +49,7 @@ export const updateItemTool: ToolConfig = body: (params) => ({ query: QUERIES.UPDATE_ITEM, variables: { - boardId: params.board_id ? parseInt(params.board_id, 10) : undefined, + boardId: parseInt(params.board_id, 10), itemId: parseInt(params.item_id, 10), columnValues: JSON.stringify(params.column_values), }, From b46a1e69e6bccf6d946868235933d0284c49544f Mon Sep 17 00:00:00 2001 From: shubhamxshah Date: Tue, 30 Dec 2025 17:03:30 +0530 Subject: [PATCH 04/10] update item --- apps/sim/app/api/tools/monday/items/route.ts | 80 +++++++++++++++++++ .../app/api/tools/monday/subitems/route.ts | 79 ++++++++++++++++++ .../file-selector/file-selector-input.tsx | 18 ++++- apps/sim/blocks/blocks/monday.ts | 29 +++++-- apps/sim/hooks/selectors/registry.ts | 50 ++++++++++++ apps/sim/hooks/selectors/resolution.ts | 8 ++ apps/sim/hooks/selectors/types.ts | 3 + apps/sim/tools/monday/graphql.ts | 24 ++++++ apps/sim/tools/monday/types.ts | 3 +- apps/sim/tools/monday/update_item.ts | 28 ++++--- 10 files changed, 303 insertions(+), 19 deletions(-) create mode 100644 apps/sim/app/api/tools/monday/items/route.ts create mode 100644 apps/sim/app/api/tools/monday/subitems/route.ts diff --git a/apps/sim/app/api/tools/monday/items/route.ts b/apps/sim/app/api/tools/monday/items/route.ts new file mode 100644 index 0000000000..5df34879f1 --- /dev/null +++ b/apps/sim/app/api/tools/monday/items/route.ts @@ -0,0 +1,80 @@ +import { createLogger } from '@sim/logger' +import { NextResponse } from 'next/server' +import { QUERIES } from '@/tools/monday/graphql' + +const logger = createLogger('MondayItemsAPI') + +/** + * POST /api/tools/monday/items + * Fetches items from a Monday.com board for selector dropdown + */ +export async function POST(request: Request) { + try { + const body = await request.json() + const { apiKey, boardId } = body + + if (!apiKey) { + logger.warn('Missing apiKey in request') + return NextResponse.json({ error: 'API key is required' }, { status: 400 }) + } + + if (!boardId) { + logger.warn('Missing boardId in request') + return NextResponse.json({ error: 'Board ID is required' }, { status: 400 }) + } + + logger.info('Fetching Monday.com items', { boardId }) + + const response = await fetch('https://api.monday.com/v2', { + method: 'POST', + headers: { + 'Content-Type': 'application/json', + Authorization: apiKey, + 'API-Version': '2024-01', + }, + body: JSON.stringify({ + query: QUERIES.GET_BOARD_ITEMS, + variables: { + boardId: [parseInt(boardId, 10)], + limit: 100, + }, + }), + }) + + if (!response.ok) { + const errorText = await response.text() + logger.error('Monday.com API error', { + status: response.status, + error: errorText, + }) + return NextResponse.json( + { error: `Monday.com API error: ${response.status}` }, + { status: response.status } + ) + } + + const result = await response.json() + + if (result.errors) { + logger.error('Monday.com GraphQL errors', { errors: result.errors }) + return NextResponse.json( + { error: 'Failed to fetch items', details: result.errors }, + { status: 400 } + ) + } + + const items = result.data?.boards?.[0]?.items_page?.items || [] + + logger.info('Successfully fetched Monday.com items', { count: items.length }) + + return NextResponse.json({ + items: items.map((item: any) => ({ + id: item.id, + name: item.name, + })), + }) + } catch (error) { + logger.error('Unexpected error fetching Monday.com items', { error }) + return NextResponse.json({ error: 'Internal server error' }, { status: 500 }) + } +} diff --git a/apps/sim/app/api/tools/monday/subitems/route.ts b/apps/sim/app/api/tools/monday/subitems/route.ts new file mode 100644 index 0000000000..aae42321fb --- /dev/null +++ b/apps/sim/app/api/tools/monday/subitems/route.ts @@ -0,0 +1,79 @@ +import { createLogger } from '@sim/logger' +import { NextResponse } from 'next/server' +import { QUERIES } from '@/tools/monday/graphql' + +const logger = createLogger('MondaySubitemsAPI') + +/** + * POST /api/tools/monday/subitems + * Fetches subitems from a Monday.com item for selector dropdown + */ +export async function POST(request: Request) { + try { + const body = await request.json() + const { apiKey, itemId } = body + + if (!apiKey) { + logger.warn('Missing apiKey in request') + return NextResponse.json({ error: 'API key is required' }, { status: 400 }) + } + + if (!itemId) { + logger.warn('Missing itemId in request') + return NextResponse.json({ error: 'Item ID is required' }, { status: 400 }) + } + + logger.info('Fetching Monday.com subitems', { itemId }) + + const response = await fetch('https://api.monday.com/v2', { + method: 'POST', + headers: { + 'Content-Type': 'application/json', + Authorization: apiKey, + 'API-Version': '2024-01', + }, + body: JSON.stringify({ + query: QUERIES.GET_ITEM_SUBITEMS, + variables: { + itemId: [parseInt(itemId, 10)], + }, + }), + }) + + if (!response.ok) { + const errorText = await response.text() + logger.error('Monday.com API error', { + status: response.status, + error: errorText, + }) + return NextResponse.json( + { error: `Monday.com API error: ${response.status}` }, + { status: response.status } + ) + } + + const result = await response.json() + + if (result.errors) { + logger.error('Monday.com GraphQL errors', { errors: result.errors }) + return NextResponse.json( + { error: 'Failed to fetch subitems', details: result.errors }, + { status: 400 } + ) + } + + const subitems = result.data?.items?.[0]?.subitems || [] + + logger.info('Successfully fetched Monday.com subitems', { count: subitems.length }) + + return NextResponse.json({ + items: subitems.map((subitem: any) => ({ + id: subitem.id, + name: subitem.name, + })), + }) + } catch (error) { + logger.error('Unexpected error fetching Monday.com subitems', { error }) + return NextResponse.json({ error: 'Internal server error' }, { status: 500 }) + } +} diff --git a/apps/sim/app/workspace/[workspaceId]/w/[workflowId]/components/panel/components/editor/components/sub-block/components/file-selector/file-selector-input.tsx b/apps/sim/app/workspace/[workspaceId]/w/[workflowId]/components/panel/components/editor/components/sub-block/components/file-selector/file-selector-input.tsx index 01ff549a7f..53169b8ce4 100644 --- a/apps/sim/app/workspace/[workspaceId]/w/[workflowId]/components/panel/components/editor/components/sub-block/components/file-selector/file-selector-input.tsx +++ b/apps/sim/app/workspace/[workspaceId]/w/[workflowId]/components/panel/components/editor/components/sub-block/components/file-selector/file-selector-input.tsx @@ -59,6 +59,8 @@ export function FileSelectorInput({ const [groupIdListFromStore] = useSubBlockValue(blockId, 'group_id_list') const [columnIdValueFromStore] = useSubBlockValue(blockId, 'column_id') const [columnIdCamelFromStore] = useSubBlockValue(blockId, 'columnId') + const [itemIdValueFromStore] = useSubBlockValue(blockId, 'item_id') + const [itemIdCamelFromStore] = useSubBlockValue(blockId, 'itemId') const connectedCredential = previewContextValues?.credential ?? connectedCredentialFromStore const domainValue = previewContextValues?.domain ?? domainValueFromStore @@ -86,6 +88,11 @@ export function FileSelectorInput({ previewContextValues?.columnId ?? columnIdValueFromStore ?? columnIdCamelFromStore + const itemIdValue = + previewContextValues?.item_id ?? + previewContextValues?.itemId ?? + itemIdValueFromStore ?? + itemIdCamelFromStore const normalizedCredentialId = typeof connectedCredential === 'string' @@ -114,6 +121,7 @@ export function FileSelectorInput({ boardId: (boardIdValue as string) || undefined, groupId: (groupIdValue as string) || undefined, columnId: (columnIdValue as string) || undefined, + itemId: (itemIdValue as string) || undefined, }) }, [ subBlock, @@ -129,6 +137,7 @@ export function FileSelectorInput({ boardIdValue, groupIdValue, columnIdValue, + itemIdValue, ]) const isMondaySelector = selectorResolution?.key?.startsWith('monday.') @@ -150,12 +159,18 @@ export function FileSelectorInput({ selectorResolution?.key === 'webflow.items' && !selectorResolution.context.collectionId const missingBoard = isMondaySelector && - (selectorResolution?.key === 'monday.groups' || selectorResolution?.key === 'monday.columns') && + (selectorResolution?.key === 'monday.groups' || + selectorResolution?.key === 'monday.columns' || + selectorResolution?.key === 'monday.items') && !selectorResolution?.context.boardId const missingColumn = isMondaySelector && selectorResolution?.key === 'monday.status-options' && !selectorResolution?.context.columnId + const missingItem = + isMondaySelector && + selectorResolution?.key === 'monday.subitems' && + !selectorResolution?.context.itemId const disabledReason = finalDisabled || @@ -169,6 +184,7 @@ export function FileSelectorInput({ missingCollection || missingBoard || missingColumn || + missingItem || !selectorResolution?.key if (!selectorResolution?.key) { diff --git a/apps/sim/blocks/blocks/monday.ts b/apps/sim/blocks/blocks/monday.ts index 44df4c5906..94ed81f61b 100644 --- a/apps/sim/blocks/blocks/monday.ts +++ b/apps/sim/blocks/blocks/monday.ts @@ -75,14 +75,6 @@ export const MondayBlock: BlockConfig = { condition: { field: 'operation', value: 'monday_create_item' }, }, // UPDATE ITEM fields - { - id: 'item_id', - title: 'Item ID', - type: 'short-input', - placeholder: 'Enter item ID to update', - required: true, - condition: { field: 'operation', value: 'monday_update_item' }, - }, { id: 'board_id_update', title: 'Board', @@ -94,6 +86,26 @@ export const MondayBlock: BlockConfig = { condition: { field: 'operation', value: 'monday_update_item' }, dependsOn: ['apiKey'], }, + { + id: 'item_id', + title: 'Item', + type: 'file-selector', + serviceId: 'monday', + placeholder: 'Select an item to update', + required: true, + condition: { field: 'operation', value: 'monday_update_item' }, + dependsOn: ['apiKey', 'board_id_update'], + }, + { + id: 'subitem_id', + title: 'Sub-Item (Optional)', + type: 'file-selector', + serviceId: 'monday', + placeholder: 'Select a sub-item to update (optional)', + required: false, + condition: { field: 'operation', value: 'monday_update_item' }, + dependsOn: ['apiKey', 'item_id'], + }, { id: 'column_values_update', title: 'Column Values (JSON)', @@ -172,6 +184,7 @@ export const MondayBlock: BlockConfig = { board_id: { type: 'string', description: 'Board ID' }, group_id: { type: 'string', description: 'Group/section ID' }, item_id: { type: 'string', description: 'Item ID' }, + subitem_id: { type: 'string', description: 'Sub-item ID' }, item_name: { type: 'string', description: 'Item name' }, column_values: { type: 'json', description: 'Column values as JSON' }, limit: { type: 'number', description: 'Maximum number of items to return' }, diff --git a/apps/sim/hooks/selectors/registry.ts b/apps/sim/hooks/selectors/registry.ts index 4979a73756..40b2f02642 100644 --- a/apps/sim/hooks/selectors/registry.ts +++ b/apps/sim/hooks/selectors/registry.ts @@ -889,6 +889,56 @@ const registry: Record = { })) }, }, + 'monday.items': { + key: 'monday.items', + staleTime: SELECTOR_STALE, + getQueryKey: ({ context }: SelectorQueryArgs) => [ + 'selectors', + 'monday.items', + context.apiKey ?? 'none', + context.boardId ?? 'none', + ], + enabled: ({ context }) => Boolean(context.apiKey && context.boardId), + fetchList: async ({ context }: SelectorQueryArgs) => { + const body = JSON.stringify({ + apiKey: context.apiKey, + boardId: context.boardId, + }) + const data = await fetchJson<{ items: { id: string; name: string }[] }>( + '/api/tools/monday/items', + { method: 'POST', body } + ) + return (data.items || []).map((item) => ({ + id: item.id, + label: item.name, + })) + }, + }, + 'monday.subitems': { + key: 'monday.subitems', + staleTime: SELECTOR_STALE, + getQueryKey: ({ context }: SelectorQueryArgs) => [ + 'selectors', + 'monday.subitems', + context.apiKey ?? 'none', + context.itemId ?? 'none', + ], + enabled: ({ context }) => Boolean(context.apiKey && context.itemId), + fetchList: async ({ context }: SelectorQueryArgs) => { + const body = JSON.stringify({ + apiKey: context.apiKey, + itemId: context.itemId, + }) + const data = await fetchJson<{ items: { id: string; name: string }[] }>( + '/api/tools/monday/subitems', + { method: 'POST', body } + ) + return (data.items || []).map((subitem) => ({ + id: subitem.id, + label: subitem.name, + })) + }, + }, } export function getSelectorDefinition(key: SelectorKey): SelectorDefinition { diff --git a/apps/sim/hooks/selectors/resolution.ts b/apps/sim/hooks/selectors/resolution.ts index f8bb92ca96..284c48d7ba 100644 --- a/apps/sim/hooks/selectors/resolution.ts +++ b/apps/sim/hooks/selectors/resolution.ts @@ -20,6 +20,7 @@ export interface SelectorResolutionArgs { apiKey?: string boardId?: string columnId?: string + itemId?: string } const defaultContext: SelectorContext = {} @@ -64,6 +65,7 @@ function buildBaseContext( apiKey: args.apiKey, boardId: args.boardId, columnId: args.columnId, + itemId: args.itemId, ...extra, } } @@ -148,6 +150,12 @@ function resolveFileSelector( if (subBlock.id === 'status_column' || subBlock.id === 'statusColumn') { return { key: 'monday.status-options', context, allowSearch: true } } + if (subBlock.id === 'item_id' || subBlock.id === 'itemId') { + return { key: 'monday.items', context, allowSearch: true } + } + if (subBlock.id === 'subitem_id' || subBlock.id === 'subitemId') { + return { key: 'monday.subitems', context, allowSearch: true } + } return { key: null, context, allowSearch: true } default: return { key: null, context, allowSearch: true } diff --git a/apps/sim/hooks/selectors/types.ts b/apps/sim/hooks/selectors/types.ts index 01b5c5306d..e8408649aa 100644 --- a/apps/sim/hooks/selectors/types.ts +++ b/apps/sim/hooks/selectors/types.ts @@ -31,6 +31,8 @@ export type SelectorKey = | 'monday.columns' | 'monday.groups' | 'monday.status-options' + | 'monday.items' + | 'monday.subitems' export interface SelectorOption { @@ -58,6 +60,7 @@ export interface SelectorContext { boardId?: string groupId?: string columnId?: string + itemId?: string } export interface SelectorQueryArgs { diff --git a/apps/sim/tools/monday/graphql.ts b/apps/sim/tools/monday/graphql.ts index 94f5d67d1c..07fd469685 100644 --- a/apps/sim/tools/monday/graphql.ts +++ b/apps/sim/tools/monday/graphql.ts @@ -207,4 +207,28 @@ export const QUERIES = { } } `, + + GET_BOARD_ITEMS: ` + query ($boardId: [ID!]!, $limit: Int) { + boards(ids: $boardId) { + items_page(limit: $limit) { + items { + id + name + } + } + } + } + `, + + GET_ITEM_SUBITEMS: ` + query ($itemId: [ID!]!) { + items(ids: $itemId) { + subitems { + id + name + } + } + } + `, } diff --git a/apps/sim/tools/monday/types.ts b/apps/sim/tools/monday/types.ts index b8a057bf75..d61379fa89 100644 --- a/apps/sim/tools/monday/types.ts +++ b/apps/sim/tools/monday/types.ts @@ -81,7 +81,8 @@ export interface CreateItemResponse extends ToolResponse { export interface UpdateItemParams { apiKey: string item_id: string - board_id?: string + subitem_id?: string + board_id: string column_values: Record } diff --git a/apps/sim/tools/monday/update_item.ts b/apps/sim/tools/monday/update_item.ts index 78923f108c..b09ec7eafb 100644 --- a/apps/sim/tools/monday/update_item.ts +++ b/apps/sim/tools/monday/update_item.ts @@ -8,7 +8,7 @@ const logger = createLogger('MondayUpdateItem') export const updateItemTool: ToolConfig = { id: 'monday_update_item', name: 'Update Monday.com Item', - description: 'Update column values in an existing Monday.com item', + description: 'Update column values in an existing Monday.com item or sub-item', version: '1.0.0', params: { @@ -24,6 +24,12 @@ export const updateItemTool: ToolConfig = visibility: 'user-or-llm', description: 'The ID of the item to update', }, + subitem_id: { + type: 'string', + required: false, + visibility: 'user-or-llm', + description: 'The ID of the sub-item to update (if updating a sub-item)', + }, board_id: { type: 'string', required: true, @@ -46,14 +52,18 @@ export const updateItemTool: ToolConfig = Authorization: params.apiKey, 'API-Version': '2024-01', }), - body: (params) => ({ - query: QUERIES.UPDATE_ITEM, - variables: { - boardId: parseInt(params.board_id, 10), - itemId: parseInt(params.item_id, 10), - columnValues: JSON.stringify(params.column_values), - }, - }), + body: (params) => { + // Use subitem_id if provided, otherwise use item_id + const targetItemId = params.subitem_id || params.item_id + return { + query: QUERIES.UPDATE_ITEM, + variables: { + boardId: parseInt(params.board_id, 10), + itemId: parseInt(targetItemId, 10), + columnValues: JSON.stringify(params.column_values), + }, + } + }, }, transformResponse: async (response: Response): Promise => { From 8767661faa5e333c5e4f387937504293714996c7 Mon Sep 17 00:00:00 2001 From: shubhamxshah Date: Tue, 30 Dec 2025 17:09:13 +0530 Subject: [PATCH 05/10] tested all endpoints --- .../file-selector/file-selector-input.tsx | 8 ++++++-- apps/sim/blocks/blocks/monday.ts | 19 ++++++++++++++++--- apps/sim/hooks/selectors/resolution.ts | 5 +++-- 3 files changed, 25 insertions(+), 7 deletions(-) diff --git a/apps/sim/app/workspace/[workspaceId]/w/[workflowId]/components/panel/components/editor/components/sub-block/components/file-selector/file-selector-input.tsx b/apps/sim/app/workspace/[workspaceId]/w/[workflowId]/components/panel/components/editor/components/sub-block/components/file-selector/file-selector-input.tsx index 53169b8ce4..d0ce42dc14 100644 --- a/apps/sim/app/workspace/[workspaceId]/w/[workflowId]/components/panel/components/editor/components/sub-block/components/file-selector/file-selector-input.tsx +++ b/apps/sim/app/workspace/[workspaceId]/w/[workflowId]/components/panel/components/editor/components/sub-block/components/file-selector/file-selector-input.tsx @@ -54,6 +54,7 @@ export function FileSelectorInput({ const [boardIdCamelFromStore] = useSubBlockValue(blockId, 'boardId') const [boardIdListFromStore] = useSubBlockValue(blockId, 'board_id_list') const [boardIdUpdateFromStore] = useSubBlockValue(blockId, 'board_id_update') + const [boardIdGetFromStore] = useSubBlockValue(blockId, 'board_id_get') const [groupIdValueFromStore] = useSubBlockValue(blockId, 'group_id') const [groupIdCamelFromStore] = useSubBlockValue(blockId, 'groupId') const [groupIdListFromStore] = useSubBlockValue(blockId, 'group_id_list') @@ -61,6 +62,7 @@ export function FileSelectorInput({ const [columnIdCamelFromStore] = useSubBlockValue(blockId, 'columnId') const [itemIdValueFromStore] = useSubBlockValue(blockId, 'item_id') const [itemIdCamelFromStore] = useSubBlockValue(blockId, 'itemId') + const [itemIdGetFromStore] = useSubBlockValue(blockId, 'item_id_get') const connectedCredential = previewContextValues?.credential ?? connectedCredentialFromStore const domainValue = previewContextValues?.domain ?? domainValueFromStore @@ -76,7 +78,8 @@ export function FileSelectorInput({ boardIdValueFromStore ?? boardIdCamelFromStore ?? boardIdListFromStore ?? - boardIdUpdateFromStore + boardIdUpdateFromStore ?? + boardIdGetFromStore const groupIdValue = previewContextValues?.group_id ?? previewContextValues?.groupId ?? @@ -92,7 +95,8 @@ export function FileSelectorInput({ previewContextValues?.item_id ?? previewContextValues?.itemId ?? itemIdValueFromStore ?? - itemIdCamelFromStore + itemIdCamelFromStore ?? + itemIdGetFromStore const normalizedCredentialId = typeof connectedCredential === 'string' diff --git a/apps/sim/blocks/blocks/monday.ts b/apps/sim/blocks/blocks/monday.ts index 94ed81f61b..ec1debd691 100644 --- a/apps/sim/blocks/blocks/monday.ts +++ b/apps/sim/blocks/blocks/monday.ts @@ -117,14 +117,27 @@ export const MondayBlock: BlockConfig = { condition: { field: 'operation', value: 'monday_update_item' }, }, // GET ITEM fields + { + id: 'board_id_get', + title: 'Board', + type: 'file-selector', + serviceId: 'monday', + canonicalParamId: 'board_id', + placeholder: 'Select a Monday.com board', + required: true, + condition: { field: 'operation', value: 'monday_get_item' }, + dependsOn: ['apiKey'], + }, { id: 'item_id_get', - title: 'Item ID', - type: 'short-input', + title: 'Item', + type: 'file-selector', + serviceId: 'monday', canonicalParamId: 'item_id', - placeholder: 'Enter item ID to retrieve', + placeholder: 'Select an item to retrieve', required: true, condition: { field: 'operation', value: 'monday_get_item' }, + dependsOn: ['apiKey', 'board_id_get'], }, // LIST ITEMS fields { diff --git a/apps/sim/hooks/selectors/resolution.ts b/apps/sim/hooks/selectors/resolution.ts index 284c48d7ba..2569bdeecb 100644 --- a/apps/sim/hooks/selectors/resolution.ts +++ b/apps/sim/hooks/selectors/resolution.ts @@ -133,7 +133,8 @@ function resolveFileSelector( subBlock.id === 'board_id' || subBlock.id === 'boardId' || subBlock.id === 'board_id_list' || - subBlock.id === 'board_id_update' + subBlock.id === 'board_id_update' || + subBlock.id === 'board_id_get' ) { return { key: 'monday.boards', context, allowSearch: true } } @@ -150,7 +151,7 @@ function resolveFileSelector( if (subBlock.id === 'status_column' || subBlock.id === 'statusColumn') { return { key: 'monday.status-options', context, allowSearch: true } } - if (subBlock.id === 'item_id' || subBlock.id === 'itemId') { + if (subBlock.id === 'item_id' || subBlock.id === 'itemId' || subBlock.id === 'item_id_get') { return { key: 'monday.items', context, allowSearch: true } } if (subBlock.id === 'subitem_id' || subBlock.id === 'subitemId') { From de29d42d181b5674b8f46e911ec16439b8ad42b2 Mon Sep 17 00:00:00 2001 From: shubhamxshah Date: Tue, 30 Dec 2025 17:18:02 +0530 Subject: [PATCH 06/10] added a sub block note for api token --- .../panel/components/editor/components/sub-block/sub-block.tsx | 3 +++ apps/sim/blocks/blocks/monday.ts | 1 + apps/sim/blocks/types.ts | 1 + 3 files changed, 5 insertions(+) diff --git a/apps/sim/app/workspace/[workspaceId]/w/[workflowId]/components/panel/components/editor/components/sub-block/sub-block.tsx b/apps/sim/app/workspace/[workspaceId]/w/[workflowId]/components/panel/components/editor/components/sub-block/sub-block.tsx index a5e6186f9d..cfac618a6a 100644 --- a/apps/sim/app/workspace/[workspaceId]/w/[workflowId]/components/panel/components/editor/components/sub-block/sub-block.tsx +++ b/apps/sim/app/workspace/[workspaceId]/w/[workflowId]/components/panel/components/editor/components/sub-block/sub-block.tsx @@ -906,6 +906,9 @@ function SubBlockComponent({ subBlockValues )} {renderInput()} + {config.note && ( +

{config.note}

+ )} ) } diff --git a/apps/sim/blocks/blocks/monday.ts b/apps/sim/blocks/blocks/monday.ts index ec1debd691..01d4a4a0d3 100644 --- a/apps/sim/blocks/blocks/monday.ts +++ b/apps/sim/blocks/blocks/monday.ts @@ -35,6 +35,7 @@ export const MondayBlock: BlockConfig = { placeholder: 'Enter your Monday.com API key', password: true, required: true, + note: 'The API key can be found by logging into Monday.com, clicking on your profile picture, opening the Developers section. Then, click on Developers in the header menu and select My Access Tokens to get your personal API token.', }, // CREATE ITEM fields { diff --git a/apps/sim/blocks/types.ts b/apps/sim/blocks/types.ts index 572593f677..0c761371f5 100644 --- a/apps/sim/blocks/types.ts +++ b/apps/sim/blocks/types.ts @@ -209,6 +209,7 @@ export interface SubBlockConfig { max?: number columns?: string[] placeholder?: string + note?: string password?: boolean readOnly?: boolean showCopyButton?: boolean From 014fcc4abeaac8a9b53ad1c40bc02397ec0dc382 Mon Sep 17 00:00:00 2001 From: shubhamxshah Date: Tue, 30 Dec 2025 17:25:57 +0530 Subject: [PATCH 07/10] icon correction --- apps/sim/blocks/blocks/monday.ts | 2 +- apps/sim/components/icons.tsx | 102 ++++++++++++++++++++++++++++--- 2 files changed, 95 insertions(+), 9 deletions(-) diff --git a/apps/sim/blocks/blocks/monday.ts b/apps/sim/blocks/blocks/monday.ts index 01d4a4a0d3..f204fce2d0 100644 --- a/apps/sim/blocks/blocks/monday.ts +++ b/apps/sim/blocks/blocks/monday.ts @@ -12,7 +12,7 @@ export const MondayBlock: BlockConfig = { 'Integrate with Monday work management platform. Create items, update column values, list items, and manage your boards programmatically.', docsLink: 'https://docs.monday.com/api', category: 'tools', - bgColor: '#FF3D57', + bgColor: '#FFFFFF', icon: MondayIcon, subBlocks: [ { diff --git a/apps/sim/components/icons.tsx b/apps/sim/components/icons.tsx index 616fb60f8c..24adf803e3 100644 --- a/apps/sim/components/icons.tsx +++ b/apps/sim/components/icons.tsx @@ -257,15 +257,101 @@ export function MondayIcon(props: SVGProps) { return ( ) } From fdd523ce624115ed51b0b2f1ca42014d1616db1e Mon Sep 17 00:00:00 2001 From: shubhamxshah Date: Tue, 30 Dec 2025 17:50:04 +0530 Subject: [PATCH 08/10] monday trigger --- apps/sim/blocks/blocks/monday.ts | 7 ++++ apps/sim/triggers/monday/column_changed.ts | 38 ++++++++++++++++++++++ apps/sim/triggers/monday/new_item.ts | 34 +++++++++++++++++++ apps/sim/triggers/monday/utils.ts | 8 +++++ 4 files changed, 87 insertions(+) create mode 100644 apps/sim/triggers/monday/utils.ts diff --git a/apps/sim/blocks/blocks/monday.ts b/apps/sim/blocks/blocks/monday.ts index f204fce2d0..b302dbcde0 100644 --- a/apps/sim/blocks/blocks/monday.ts +++ b/apps/sim/blocks/blocks/monday.ts @@ -2,12 +2,14 @@ import { MondayIcon } from '@/components/icons' import type { BlockConfig } from '@/blocks/types' import { AuthMode } from '@/blocks/types' import type { MondayResponse } from '@/tools/monday/types' +import { getTrigger } from '@/triggers' export const MondayBlock: BlockConfig = { type: 'monday', name: 'Monday', description: 'Create and manage items on Monday boards', authMode: AuthMode.ApiKey, + triggerAllowed: true, longDescription: 'Integrate with Monday work management platform. Create items, update column values, list items, and manage your boards programmatically.', docsLink: 'https://docs.monday.com/api', @@ -174,6 +176,11 @@ export const MondayBlock: BlockConfig = { required: false, condition: { field: 'operation', value: 'monday_list_items' }, }, + // Trigger subBlocks + // Include all fields from first trigger (has selectedTriggerId + all fields with conditions) + ...getTrigger('monday_new_item').subBlocks, + // Skip only selectedTriggerId from second trigger (index 0), keep apiKey and rest + ...getTrigger('monday_column_changed').subBlocks.slice(1), ], tools: { access: [ diff --git a/apps/sim/triggers/monday/column_changed.ts b/apps/sim/triggers/monday/column_changed.ts index 37242ede9a..5c317ed17b 100644 --- a/apps/sim/triggers/monday/column_changed.ts +++ b/apps/sim/triggers/monday/column_changed.ts @@ -1,6 +1,7 @@ import { MondayIcon } from '@/components/icons' import { createLogger } from '@sim/logger' import type { TriggerConfig } from '@/triggers/types' +import { mondayTriggerOptions } from './utils' const logger = createLogger('MondayColumnChangedTrigger') @@ -13,6 +14,15 @@ export const mondayColumnChangedTrigger: TriggerConfig = { icon: MondayIcon, subBlocks: [ + { + id: 'selectedTriggerId', + title: 'Trigger Type', + type: 'dropdown', + mode: 'trigger', + options: mondayTriggerOptions, + value: () => 'monday_column_changed', + required: true, + }, { id: 'apiKey', title: 'API Key', @@ -22,6 +32,10 @@ export const mondayColumnChangedTrigger: TriggerConfig = { password: true, required: true, mode: 'trigger', + condition: { + field: 'selectedTriggerId', + value: 'monday_column_changed', + }, }, { id: 'boardId', @@ -33,6 +47,10 @@ export const mondayColumnChangedTrigger: TriggerConfig = { required: true, dependsOn: ['apiKey'], mode: 'trigger', + condition: { + field: 'selectedTriggerId', + value: 'monday_column_changed', + }, }, { id: 'columnId', @@ -44,6 +62,10 @@ export const mondayColumnChangedTrigger: TriggerConfig = { required: true, dependsOn: ['apiKey', 'boardId'], mode: 'trigger', + condition: { + field: 'selectedTriggerId', + value: 'monday_column_changed', + }, }, { id: 'specificValue', @@ -53,6 +75,10 @@ export const mondayColumnChangedTrigger: TriggerConfig = { description: 'Only trigger when column changes to this specific value (optional)', required: false, mode: 'trigger', + condition: { + field: 'selectedTriggerId', + value: 'monday_column_changed', + }, }, { id: 'pollingInterval', @@ -68,6 +94,10 @@ export const mondayColumnChangedTrigger: TriggerConfig = { description: 'How often to check for column changes', required: true, mode: 'trigger', + condition: { + field: 'selectedTriggerId', + value: 'monday_column_changed', + }, }, { id: 'triggerInstructions', @@ -84,6 +114,10 @@ export const mondayColumnChangedTrigger: TriggerConfig = { .map((instruction, index) => `${index + 1}. ${instruction}`) .join('\n'), mode: 'trigger', + condition: { + field: 'selectedTriggerId', + value: 'monday_column_changed', + }, }, { id: 'triggerSave', @@ -92,6 +126,10 @@ export const mondayColumnChangedTrigger: TriggerConfig = { hideFromPreview: true, mode: 'trigger', triggerId: 'monday_column_changed', + condition: { + field: 'selectedTriggerId', + value: 'monday_column_changed', + }, }, ], diff --git a/apps/sim/triggers/monday/new_item.ts b/apps/sim/triggers/monday/new_item.ts index 581ca5ebf3..71062cdd35 100644 --- a/apps/sim/triggers/monday/new_item.ts +++ b/apps/sim/triggers/monday/new_item.ts @@ -1,6 +1,7 @@ import { MondayIcon } from '@/components/icons' import { createLogger } from '@sim/logger' import type { TriggerConfig } from '@/triggers/types' +import { mondayTriggerOptions } from './utils' const logger = createLogger('MondayNewItemTrigger') @@ -13,6 +14,15 @@ export const mondayNewItemTrigger: TriggerConfig = { icon: MondayIcon, subBlocks: [ + { + id: 'selectedTriggerId', + title: 'Trigger Type', + type: 'dropdown', + mode: 'trigger', + options: mondayTriggerOptions, + value: () => 'monday_new_item', + required: true, + }, { id: 'apiKey', title: 'API Key', @@ -22,6 +32,10 @@ export const mondayNewItemTrigger: TriggerConfig = { password: true, required: true, mode: 'trigger', + condition: { + field: 'selectedTriggerId', + value: 'monday_new_item', + }, }, { id: 'boardId', @@ -33,6 +47,10 @@ export const mondayNewItemTrigger: TriggerConfig = { required: true, dependsOn: ['apiKey'], mode: 'trigger', + condition: { + field: 'selectedTriggerId', + value: 'monday_new_item', + }, }, { id: 'groupId', @@ -44,6 +62,10 @@ export const mondayNewItemTrigger: TriggerConfig = { required: false, dependsOn: ['apiKey', 'boardId'], mode: 'trigger', + condition: { + field: 'selectedTriggerId', + value: 'monday_new_item', + }, }, { id: 'pollingInterval', @@ -59,6 +81,10 @@ export const mondayNewItemTrigger: TriggerConfig = { description: 'How often to check for new items', required: true, mode: 'trigger', + condition: { + field: 'selectedTriggerId', + value: 'monday_new_item', + }, }, { id: 'triggerInstructions', @@ -75,6 +101,10 @@ export const mondayNewItemTrigger: TriggerConfig = { .map((instruction, index) => `${index + 1}. ${instruction}`) .join('\n'), mode: 'trigger', + condition: { + field: 'selectedTriggerId', + value: 'monday_new_item', + }, }, { id: 'triggerSave', @@ -83,6 +113,10 @@ export const mondayNewItemTrigger: TriggerConfig = { hideFromPreview: true, mode: 'trigger', triggerId: 'monday_new_item', + condition: { + field: 'selectedTriggerId', + value: 'monday_new_item', + }, }, ], diff --git a/apps/sim/triggers/monday/utils.ts b/apps/sim/triggers/monday/utils.ts new file mode 100644 index 0000000000..4c2b1d4a1c --- /dev/null +++ b/apps/sim/triggers/monday/utils.ts @@ -0,0 +1,8 @@ +/** + * Shared utilities for Monday.com triggers + */ + +export const mondayTriggerOptions = [ + { label: 'New Item', id: 'monday_new_item' }, + { label: 'Column Changed', id: 'monday_column_changed' }, +] From 7af2be2942f03539eae5979c0c8a427dd2431e15 Mon Sep 17 00:00:00 2001 From: shubhamxshah Date: Tue, 30 Dec 2025 18:24:49 +0530 Subject: [PATCH 09/10] tested trigger --- apps/sim/lib/webhooks/processor.ts | 6 ++++ apps/sim/lib/webhooks/provider-utils.ts | 13 ++++++++ apps/sim/lib/webhooks/utils.server.ts | 12 +++++++ apps/sim/triggers/monday/column_changed.ts | 37 ++++++++++++---------- apps/sim/triggers/monday/new_item.ts | 37 ++++++++++++---------- 5 files changed, 71 insertions(+), 34 deletions(-) diff --git a/apps/sim/lib/webhooks/processor.ts b/apps/sim/lib/webhooks/processor.ts index b197a8ef18..8a383a1c37 100644 --- a/apps/sim/lib/webhooks/processor.ts +++ b/apps/sim/lib/webhooks/processor.ts @@ -8,6 +8,7 @@ import { isTriggerDevEnabled } from '@/lib/core/config/feature-flags' import { preprocessExecution } from '@/lib/execution/preprocessing' import { convertSquareBracketsToTwiML } from '@/lib/webhooks/utils' import { + handleMondayChallenge, handleSlackChallenge, handleWhatsAppVerification, validateMicrosoftTeamsSignature, @@ -108,6 +109,11 @@ export async function handleProviderChallenges( return slackResponse } + const mondayResponse = handleMondayChallenge(body) + if (mondayResponse) { + return mondayResponse + } + const url = new URL(request.url) const mode = url.searchParams.get('hub.mode') const token = url.searchParams.get('hub.verify_token') diff --git a/apps/sim/lib/webhooks/provider-utils.ts b/apps/sim/lib/webhooks/provider-utils.ts index 2ae0681e94..742cd47e64 100644 --- a/apps/sim/lib/webhooks/provider-utils.ts +++ b/apps/sim/lib/webhooks/provider-utils.ts @@ -63,6 +63,18 @@ function extractAirtableIdentifier(body: any): string | null { return null } +function extractMondayIdentifier(body: any): string | null { + // Monday.com sends a triggerUuid for each webhook event + if (body.event?.triggerUuid) { + return body.event.triggerUuid + } + // Fallback to pulseId + triggerTime combination + if (body.event?.pulseId && body.event?.triggerTime) { + return `${body.event.pulseId}:${body.event.triggerTime}` + } + return null +} + const PROVIDER_EXTRACTORS: Record string | null> = { slack: extractSlackIdentifier, twilio: extractTwilioIdentifier, @@ -73,6 +85,7 @@ const PROVIDER_EXTRACTORS: Record string | null> = { jira: extractJiraIdentifier, 'microsoft-teams': extractMicrosoftTeamsIdentifier, airtable: extractAirtableIdentifier, + monday: extractMondayIdentifier, } export function extractProviderIdentifierFromBody(provider: string, body: any): string | null { diff --git a/apps/sim/lib/webhooks/utils.server.ts b/apps/sim/lib/webhooks/utils.server.ts index 49e69649df..a0131b52e9 100644 --- a/apps/sim/lib/webhooks/utils.server.ts +++ b/apps/sim/lib/webhooks/utils.server.ts @@ -69,6 +69,18 @@ export function handleSlackChallenge(body: any): NextResponse | null { return null } +/** + * Handles Monday.com webhook challenge verification + * Monday.com sends a challenge field that must be echoed back + */ +export function handleMondayChallenge(body: any): NextResponse | null { + if (body?.challenge) { + return NextResponse.json({ challenge: body.challenge }) + } + + return null +} + /** * Fetches a URL with DNS pinning to prevent DNS rebinding attacks * @param url - The URL to fetch diff --git a/apps/sim/triggers/monday/column_changed.ts b/apps/sim/triggers/monday/column_changed.ts index 5c317ed17b..fde08cc971 100644 --- a/apps/sim/triggers/monday/column_changed.ts +++ b/apps/sim/triggers/monday/column_changed.ts @@ -81,18 +81,13 @@ export const mondayColumnChangedTrigger: TriggerConfig = { }, }, { - id: 'pollingInterval', - title: 'Polling Interval', - type: 'dropdown', - options: [ - { label: 'Every 5 minutes', id: '5' }, - { label: 'Every 15 minutes', id: '15' }, - { label: 'Every 30 minutes', id: '30' }, - { label: 'Every hour', id: '60' }, - ], - defaultValue: '15', - description: 'How often to check for column changes', - required: true, + id: 'webhookUrlDisplay', + title: 'Webhook URL', + type: 'short-input', + readOnly: true, + showCopyButton: true, + useWebhookUrl: true, + placeholder: 'Webhook URL will be generated', mode: 'trigger', condition: { field: 'selectedTriggerId', @@ -105,11 +100,11 @@ export const mondayColumnChangedTrigger: TriggerConfig = { hideFromPreview: true, type: 'text', defaultValue: [ - 'Get your Monday.com API key from Settings > Admin > API', - 'Select the board and column you want to monitor', - 'Optionally specify a specific value to trigger on', - 'The trigger will detect changes at the specified interval', - 'Each change will include both old and new values', + 'Get your Monday.com API key from Clicking your profile picture > Developers > API Access Tokens.', + 'Copy the Webhook URL above', + 'Select the board and click on automate on the right', + "search for webhook and paste the copied webhook url", + 'The webhook will send real-time notifications when column values change', ] .map((instruction, index) => `${index + 1}. ${instruction}`) .join('\n'), @@ -164,4 +159,12 @@ export const mondayColumnChangedTrigger: TriggerConfig = { description: 'Trigger timestamp', }, }, + + webhook: { + method: 'POST', + headers: { + 'Content-Type': 'application/json', + 'User-Agent': 'monday.com', + }, + }, } diff --git a/apps/sim/triggers/monday/new_item.ts b/apps/sim/triggers/monday/new_item.ts index 71062cdd35..1d6e0c856f 100644 --- a/apps/sim/triggers/monday/new_item.ts +++ b/apps/sim/triggers/monday/new_item.ts @@ -68,18 +68,13 @@ export const mondayNewItemTrigger: TriggerConfig = { }, }, { - id: 'pollingInterval', - title: 'Polling Interval', - type: 'dropdown', - options: [ - { label: 'Every 5 minutes', id: '5' }, - { label: 'Every 15 minutes', id: '15' }, - { label: 'Every 30 minutes', id: '30' }, - { label: 'Every hour', id: '60' }, - ], - defaultValue: '15', - description: 'How often to check for new items', - required: true, + id: 'webhookUrlDisplay', + title: 'Webhook URL', + type: 'short-input', + readOnly: true, + showCopyButton: true, + useWebhookUrl: true, + placeholder: 'Webhook URL will be generated', mode: 'trigger', condition: { field: 'selectedTriggerId', @@ -92,11 +87,11 @@ export const mondayNewItemTrigger: TriggerConfig = { hideFromPreview: true, type: 'text', defaultValue: [ - 'Get your Monday.com API key from Settings > Admin > API', - 'Select the board you want to monitor', - 'Optionally filter by a specific group', - 'The trigger will check for new items at the specified interval', - 'New items will be detected based on their creation time', + 'Get your Monday.com API key from Clicking your profile picture > Developers > API Access Tokens.', + 'Copy the Webhook URL above', + 'Select the board and click on automate on the right', + "search for webhook and paste the copied webhook url", + 'The webhook will send real-time notifications when new item is added', ] .map((instruction, index) => `${index + 1}. ${instruction}`) .join('\n'), @@ -135,4 +130,12 @@ export const mondayNewItemTrigger: TriggerConfig = { description: 'Trigger timestamp', }, }, + + webhook: { + method: 'POST', + headers: { + 'Content-Type': 'application/json', + 'User-Agent': 'monday.com', + }, + }, } From 611b28ee17234338647adeb33978162b673472f1 Mon Sep 17 00:00:00 2001 From: shubhamxshah Date: Tue, 30 Dec 2025 19:21:58 +0530 Subject: [PATCH 10/10] fixed greptile issues --- apps/sim/app/api/tools/monday/boards/route.ts | 12 ++++--- .../sim/app/api/tools/monday/columns/route.ts | 15 +++++---- apps/sim/app/api/tools/monday/items/route.ts | 7 +++- .../api/tools/monday/status-options/route.ts | 25 +++++++++----- .../app/api/tools/monday/subitems/route.ts | 7 +++- .../selector-combobox/selector-combobox.tsx | 33 ++++++++++++++----- .../sub-block/hooks/use-depends-on-gate.ts | 5 ++- .../editor/components/sub-block/sub-block.tsx | 9 +++-- .../lib/workflows/executor/execution-core.ts | 9 +++-- apps/sim/tools/monday/create_item.ts | 2 +- apps/sim/tools/monday/get_item.ts | 2 +- apps/sim/tools/monday/graphql.ts | 23 ------------- apps/sim/tools/monday/list_items.ts | 6 ++-- apps/sim/tools/monday/update_item.ts | 2 +- apps/sim/triggers/monday/new_item.ts | 3 -- 15 files changed, 93 insertions(+), 67 deletions(-) diff --git a/apps/sim/app/api/tools/monday/boards/route.ts b/apps/sim/app/api/tools/monday/boards/route.ts index 11a212acae..ea7d412590 100644 --- a/apps/sim/app/api/tools/monday/boards/route.ts +++ b/apps/sim/app/api/tools/monday/boards/route.ts @@ -15,6 +15,13 @@ interface MondayBoard { state: string } +/** + * POST /api/tools/monday/boards + * Fetches active boards from a Monday.com account + * + * @param request - Request containing the Monday.com API key + * @returns JSON response with list of active boards + */ export async function POST(request: Request) { try { const requestId = generateRequestId() @@ -45,9 +52,6 @@ export async function POST(request: Request) { return NextResponse.json({ items: boards }) } catch (error) { logger.error('Error fetching Monday.com boards:', error) - return NextResponse.json( - { error: 'Failed to retrieve Monday.com boards', details: (error as Error).message }, - { status: 500 } - ) + return NextResponse.json({ error: 'Failed to retrieve Monday.com boards' }, { status: 500 }) } } diff --git a/apps/sim/app/api/tools/monday/columns/route.ts b/apps/sim/app/api/tools/monday/columns/route.ts index b5a7f6981a..11883473c7 100644 --- a/apps/sim/app/api/tools/monday/columns/route.ts +++ b/apps/sim/app/api/tools/monday/columns/route.ts @@ -30,13 +30,19 @@ export async function POST(request: Request) { return NextResponse.json({ error: 'Board ID is required' }, { status: 400 }) } - logger.info('Fetching Monday.com columns', { requestId, boardId }) + const parsedBoardId = parseInt(boardId, 10) + if (isNaN(parsedBoardId)) { + logger.error('Invalid board ID format', { boardId }) + return NextResponse.json({ error: 'Board ID must be a valid number' }, { status: 400 }) + } + + logger.info('Fetching Monday.com columns', { requestId, boardId: parsedBoardId }) const data = await executeMondayQuery<{ boards: Array<{ columns: MondayColumn[] }> }>( apiKey, { query: QUERIES.GET_BOARD_COLUMNS, - variables: { boardId: [parseInt(boardId, 10)] }, + variables: { boardId: [parsedBoardId] }, } ) @@ -51,9 +57,6 @@ export async function POST(request: Request) { return NextResponse.json({ items: formattedColumns }) } catch (error) { logger.error('Error fetching Monday.com columns:', error) - return NextResponse.json( - { error: 'Failed to retrieve columns', details: (error as Error).message }, - { status: 500 } - ) + return NextResponse.json({ error: 'Failed to retrieve columns' }, { status: 500 }) } } diff --git a/apps/sim/app/api/tools/monday/items/route.ts b/apps/sim/app/api/tools/monday/items/route.ts index 5df34879f1..c977c53457 100644 --- a/apps/sim/app/api/tools/monday/items/route.ts +++ b/apps/sim/app/api/tools/monday/items/route.ts @@ -4,6 +4,11 @@ import { QUERIES } from '@/tools/monday/graphql' const logger = createLogger('MondayItemsAPI') +interface MondayItem { + id: string + name: string +} + /** * POST /api/tools/monday/items * Fetches items from a Monday.com board for selector dropdown @@ -68,7 +73,7 @@ export async function POST(request: Request) { logger.info('Successfully fetched Monday.com items', { count: items.length }) return NextResponse.json({ - items: items.map((item: any) => ({ + items: items.map((item: MondayItem) => ({ id: item.id, name: item.name, })), diff --git a/apps/sim/app/api/tools/monday/status-options/route.ts b/apps/sim/app/api/tools/monday/status-options/route.ts index 200342b28d..aa26c63b44 100644 --- a/apps/sim/app/api/tools/monday/status-options/route.ts +++ b/apps/sim/app/api/tools/monday/status-options/route.ts @@ -20,6 +20,11 @@ interface StatusLabel { color: string } +interface StatusLabelSettings { + label: string + color?: string +} + export async function POST(request: Request) { try { const requestId = generateRequestId() @@ -66,11 +71,16 @@ export async function POST(request: Request) { const settings = JSON.parse(column.settings_str) const labels = settings.labels || {} - statusOptions = Object.entries(labels).map(([id, label]: [string, any]) => ({ - id, - label: label.label || label, - color: label.color || '#000000', - })) + statusOptions = Object.entries(labels).map(([id, label]: [string, StatusLabelSettings | string]) => { + if (typeof label === 'string') { + return { id, label, color: '#000000' } + } + return { + id, + label: label.label, + color: label.color || '#000000', + } + }) } catch (parseError) { logger.error('Failed to parse column settings', { error: parseError, @@ -89,9 +99,6 @@ export async function POST(request: Request) { }) } catch (error) { logger.error('Error fetching Monday.com status options:', error) - return NextResponse.json( - { error: 'Failed to retrieve status options', details: (error as Error).message }, - { status: 500 } - ) + return NextResponse.json({ error: 'Failed to retrieve status options' }, { status: 500 }) } } diff --git a/apps/sim/app/api/tools/monday/subitems/route.ts b/apps/sim/app/api/tools/monday/subitems/route.ts index aae42321fb..52ead996b4 100644 --- a/apps/sim/app/api/tools/monday/subitems/route.ts +++ b/apps/sim/app/api/tools/monday/subitems/route.ts @@ -4,6 +4,11 @@ import { QUERIES } from '@/tools/monday/graphql' const logger = createLogger('MondaySubitemsAPI') +interface MondaySubitem { + id: string + name: string +} + /** * POST /api/tools/monday/subitems * Fetches subitems from a Monday.com item for selector dropdown @@ -67,7 +72,7 @@ export async function POST(request: Request) { logger.info('Successfully fetched Monday.com subitems', { count: subitems.length }) return NextResponse.json({ - items: subitems.map((subitem: any) => ({ + items: subitems.map((subitem: MondaySubitem) => ({ id: subitem.id, name: subitem.name, })), diff --git a/apps/sim/app/workspace/[workspaceId]/w/[workflowId]/components/panel/components/editor/components/sub-block/components/selector-combobox/selector-combobox.tsx b/apps/sim/app/workspace/[workspaceId]/w/[workflowId]/components/panel/components/editor/components/sub-block/components/selector-combobox/selector-combobox.tsx index df9096ee9e..7ecef4f6df 100644 --- a/apps/sim/app/workspace/[workspaceId]/w/[workflowId]/components/panel/components/editor/components/sub-block/components/selector-combobox/selector-combobox.tsx +++ b/apps/sim/app/workspace/[workspaceId]/w/[workflowId]/components/panel/components/editor/components/sub-block/components/selector-combobox/selector-combobox.tsx @@ -1,5 +1,6 @@ import type React from 'react' import { useCallback, useEffect, useMemo, useRef, useState } from 'react' +import { createLogger } from '@sim/logger' import { Combobox as EditableCombobox } from '@/components/emcn/components' import { SubBlockInputController } from '@/app/workspace/[workspaceId]/w/[workflowId]/components/panel/components/editor/components/sub-block/components/sub-block-input-controller' import { useSubBlockValue } from '@/app/workspace/[workspaceId]/w/[workflowId]/components/panel/components/editor/components/sub-block/hooks/use-sub-block-value' @@ -11,6 +12,8 @@ import { useSelectorOptions, } from '@/hooks/selectors/use-selector-query' +const logger = createLogger('SelectorCombobox') + interface SelectorComboboxProps { blockId: string subBlock: SubBlockConfig @@ -46,12 +49,26 @@ export function SelectorCombobox({ const [columnIdCamelFromBlock] = useSubBlockValue(blockId, 'columnId') // Merge Monday.com specific values into context if they're missing - const enrichedContext = selectorKey.startsWith('monday.') ? { - ...selectorContext, - apiKey: selectorContext.apiKey || apiKeyFromBlock, - boardId: selectorContext.boardId || boardIdFromBlock || boardIdCamelFromBlock, - columnId: selectorContext.columnId || columnIdFromBlock || columnIdCamelFromBlock, - } : selectorContext + const enrichedContext = useMemo( + () => + selectorKey.startsWith('monday.') + ? { + ...selectorContext, + apiKey: selectorContext.apiKey || apiKeyFromBlock, + boardId: selectorContext.boardId || boardIdFromBlock || boardIdCamelFromBlock, + columnId: selectorContext.columnId || columnIdFromBlock || columnIdCamelFromBlock, + } + : selectorContext, + [ + selectorKey, + selectorContext, + apiKeyFromBlock, + boardIdFromBlock, + boardIdCamelFromBlock, + columnIdFromBlock, + columnIdCamelFromBlock, + ] + ) // For Monday selectors, override disabled if we have apiKey and required dependencies let actualDisabled = disabled @@ -68,14 +85,14 @@ export function SelectorCombobox({ } } - console.log('[SelectorCombobox RENDER]', { + logger.info('SelectorCombobox render', { subBlockId: subBlock.id, selectorKey, disabled, actualDisabled, hasApiKey: !!enrichedContext.apiKey, apiKeyFromBlock, - enrichedContext + enrichedContext, }) const [storeValueRaw, setStoreValue] = useSubBlockValue( diff --git a/apps/sim/app/workspace/[workspaceId]/w/[workflowId]/components/panel/components/editor/components/sub-block/hooks/use-depends-on-gate.ts b/apps/sim/app/workspace/[workspaceId]/w/[workflowId]/components/panel/components/editor/components/sub-block/hooks/use-depends-on-gate.ts index 2c1583ab83..3c22641474 100644 --- a/apps/sim/app/workspace/[workspaceId]/w/[workflowId]/components/panel/components/editor/components/sub-block/hooks/use-depends-on-gate.ts +++ b/apps/sim/app/workspace/[workspaceId]/w/[workflowId]/components/panel/components/editor/components/sub-block/hooks/use-depends-on-gate.ts @@ -1,10 +1,13 @@ 'use client' import { useMemo } from 'react' +import { createLogger } from '@sim/logger' import type { SubBlockConfig } from '@/blocks/types' import { useWorkflowRegistry } from '@/stores/workflows/registry/store' import { useSubBlockStore } from '@/stores/workflows/subblock/store' +const logger = createLogger('useDependsOnGate') + type DependsOnConfig = string[] | { all?: string[]; any?: string[] } /** @@ -146,7 +149,7 @@ export function useDependsOnGate( // Debug logging for dependency issues if (typeof window !== 'undefined' && allDependsOnFields.includes('apiKey')) { - console.log('[useDependsOnGate Debug]', { + logger.info('Dependency gate debug', { subBlockId: subBlock.id, allDependsOnFields, dependencyValuesMap, diff --git a/apps/sim/app/workspace/[workspaceId]/w/[workflowId]/components/panel/components/editor/components/sub-block/sub-block.tsx b/apps/sim/app/workspace/[workspaceId]/w/[workflowId]/components/panel/components/editor/components/sub-block/sub-block.tsx index cfac618a6a..1478255db5 100644 --- a/apps/sim/app/workspace/[workspaceId]/w/[workflowId]/components/panel/components/editor/components/sub-block/sub-block.tsx +++ b/apps/sim/app/workspace/[workspaceId]/w/[workflowId]/components/panel/components/editor/components/sub-block/sub-block.tsx @@ -1,5 +1,6 @@ import { type JSX, type MouseEvent, memo, useRef, useState } from 'react' import { AlertTriangle, Wand2 } from 'lucide-react' +import { createLogger } from '@sim/logger' import { Label, Tooltip } from '@/components/emcn/components' import { Button } from '@/components/ui/button' import { cn } from '@/lib/core/utils/cn' @@ -44,6 +45,8 @@ import { import { useDependsOnGate } from '@/app/workspace/[workspaceId]/w/[workflowId]/components/panel/components/editor/components/sub-block/hooks/use-depends-on-gate' import type { SubBlockConfig } from '@/blocks/types' +const logger = createLogger('SubBlock') + /** * Interface for wand control handlers exposed by sub-block inputs */ @@ -402,7 +405,7 @@ function SubBlockComponent({ // Debug logging for Monday selector issues if (config.serviceId === 'monday' && typeof window !== 'undefined') { - console.log('[Sub-block Debug]', { + logger.info('Sub-block debug', { subBlockId: config.id, type: config.type, dependsOn: config.dependsOn, @@ -669,11 +672,11 @@ function SubBlockComponent({ ) case 'file-selector': - console.log('[SUB-BLOCK] Rendering file-selector', { + logger.info('Rendering file-selector', { subBlockId: config.id, serviceId: config.serviceId, isDisabled, - dependsOn: config.dependsOn + dependsOn: config.dependsOn, }) return ( { - acc[key] = (sb as any).value + acc[key] = (sb as SubBlockState).value return acc - }, {} as Record), + }, {} as Record), }) } }) diff --git a/apps/sim/tools/monday/create_item.ts b/apps/sim/tools/monday/create_item.ts index cc2e9b7b65..601cd03bd6 100644 --- a/apps/sim/tools/monday/create_item.ts +++ b/apps/sim/tools/monday/create_item.ts @@ -15,7 +15,7 @@ export const createItemTool: ToolConfig = apiKey: { type: 'string', required: true, - visibility: 'hidden', + visibility: 'user-only', description: 'Monday.com API key', }, board_id: { diff --git a/apps/sim/tools/monday/get_item.ts b/apps/sim/tools/monday/get_item.ts index c40fefc518..a356de5438 100644 --- a/apps/sim/tools/monday/get_item.ts +++ b/apps/sim/tools/monday/get_item.ts @@ -15,7 +15,7 @@ export const getItemTool: ToolConfig = { apiKey: { type: 'string', required: true, - visibility: 'hidden', + visibility: 'user-only', description: 'Monday.com API key', }, item_id: { diff --git a/apps/sim/tools/monday/graphql.ts b/apps/sim/tools/monday/graphql.ts index 07fd469685..9de0e595eb 100644 --- a/apps/sim/tools/monday/graphql.ts +++ b/apps/sim/tools/monday/graphql.ts @@ -172,29 +172,6 @@ export const QUERIES = { } `, - LIST_ITEMS_NO_FILTER: ` - query ($boardId: [ID!]!, $limit: Int) { - boards(ids: $boardId) { - items_page(limit: $limit) { - items { - id - name - created_at - updated_at - board { id } - group { id } - column_values { - id - type - text - value - } - } - } - } - } - `, - GET_COLUMN_SETTINGS: ` query ($boardId: [ID!]!, $columnId: String!) { boards(ids: $boardId) { diff --git a/apps/sim/tools/monday/list_items.ts b/apps/sim/tools/monday/list_items.ts index 6ce4aaf788..ba1b5ea9f8 100644 --- a/apps/sim/tools/monday/list_items.ts +++ b/apps/sim/tools/monday/list_items.ts @@ -1,4 +1,4 @@ -import type { ListItemsParams, ListItemsResponse } from '@/tools/monday/types' +import type { ListItemsParams, ListItemsResponse, MondayItem } from '@/tools/monday/types' import type { ToolConfig } from '@/tools/types' import { createLogger } from '@sim/logger' import { QUERIES } from './graphql' @@ -15,7 +15,7 @@ export const listItemsTool: ToolConfig = { apiKey: { type: 'string', required: true, - visibility: 'hidden', + visibility: 'user-only', description: 'Monday.com API key', }, board_id: { @@ -86,7 +86,7 @@ export const listItemsTool: ToolConfig = { // Filter by group if group_id is provided if (params.group_id) { - items = items.filter((item: any) => item.group?.id === params.group_id) + items = items.filter((item: MondayItem) => item.group?.id === params.group_id) } logger.info('Monday items listed successfully', { count: items.length, filtered: !!params.group_id }) diff --git a/apps/sim/tools/monday/update_item.ts b/apps/sim/tools/monday/update_item.ts index b09ec7eafb..e64da9afce 100644 --- a/apps/sim/tools/monday/update_item.ts +++ b/apps/sim/tools/monday/update_item.ts @@ -15,7 +15,7 @@ export const updateItemTool: ToolConfig = apiKey: { type: 'string', required: true, - visibility: 'hidden', + visibility: 'user-only', description: 'Monday.com API key', }, item_id: { diff --git a/apps/sim/triggers/monday/new_item.ts b/apps/sim/triggers/monday/new_item.ts index 1d6e0c856f..29eb855ca2 100644 --- a/apps/sim/triggers/monday/new_item.ts +++ b/apps/sim/triggers/monday/new_item.ts @@ -1,10 +1,7 @@ import { MondayIcon } from '@/components/icons' -import { createLogger } from '@sim/logger' import type { TriggerConfig } from '@/triggers/types' import { mondayTriggerOptions } from './utils' -const logger = createLogger('MondayNewItemTrigger') - export const mondayNewItemTrigger: TriggerConfig = { id: 'monday_new_item', name: 'Monday.com New Item',