mirror of
https://github.com/simstudioai/sim.git
synced 2026-01-09 15:07:55 -05:00
Compare commits
1 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
09cea81ae3 |
@@ -0,0 +1,86 @@
|
||||
import { Button } from '@/components/emcn'
|
||||
import { Alert, AlertDescription } from '@/components/ui/alert'
|
||||
import type { SaveStatus } from '@/hooks/use-auto-save'
|
||||
|
||||
interface SaveStatusIndicatorProps {
|
||||
/** Current save status */
|
||||
status: SaveStatus
|
||||
/** Error message to display */
|
||||
errorMessage: string | null
|
||||
/** Text to show while saving (e.g., "Saving schedule...") */
|
||||
savingText?: string
|
||||
/** Text to show while loading (e.g., "Loading schedule...") */
|
||||
loadingText?: string
|
||||
/** Whether to show loading indicator */
|
||||
isLoading?: boolean
|
||||
/** Callback when retry button is clicked */
|
||||
onRetry?: () => void
|
||||
/** Whether retry is disabled (e.g., during saving) */
|
||||
retryDisabled?: boolean
|
||||
/** Number of retry attempts made */
|
||||
retryCount?: number
|
||||
/** Maximum retry attempts allowed */
|
||||
maxRetries?: number
|
||||
}
|
||||
|
||||
/**
|
||||
* Shared component for displaying save status indicators.
|
||||
* Shows saving spinner, error alerts with retry, and loading indicators.
|
||||
*/
|
||||
export function SaveStatusIndicator({
|
||||
status,
|
||||
errorMessage,
|
||||
savingText = 'Saving...',
|
||||
loadingText = 'Loading...',
|
||||
isLoading = false,
|
||||
onRetry,
|
||||
retryDisabled = false,
|
||||
retryCount = 0,
|
||||
maxRetries = 3,
|
||||
}: SaveStatusIndicatorProps) {
|
||||
const maxRetriesReached = retryCount >= maxRetries
|
||||
|
||||
return (
|
||||
<>
|
||||
{/* Saving indicator */}
|
||||
{status === 'saving' && (
|
||||
<div className='flex items-center gap-2 text-muted-foreground text-sm'>
|
||||
<div className='h-4 w-4 animate-spin rounded-full border-[1.5px] border-current border-t-transparent' />
|
||||
{savingText}
|
||||
</div>
|
||||
)}
|
||||
|
||||
{/* Error message with retry */}
|
||||
{errorMessage && (
|
||||
<Alert variant='destructive'>
|
||||
<AlertDescription className='flex items-center justify-between'>
|
||||
<span>
|
||||
{errorMessage}
|
||||
{maxRetriesReached && (
|
||||
<span className='ml-1 text-xs opacity-75'>(Max retries reached)</span>
|
||||
)}
|
||||
</span>
|
||||
{onRetry && (
|
||||
<Button
|
||||
variant='ghost'
|
||||
onClick={onRetry}
|
||||
disabled={retryDisabled || status === 'saving'}
|
||||
className='ml-2 h-6 px-2 text-xs'
|
||||
>
|
||||
Retry
|
||||
</Button>
|
||||
)}
|
||||
</AlertDescription>
|
||||
</Alert>
|
||||
)}
|
||||
|
||||
{/* Loading indicator */}
|
||||
{isLoading && status !== 'saving' && (
|
||||
<div className='flex items-center gap-2 text-muted-foreground text-sm'>
|
||||
<div className='h-4 w-4 animate-spin rounded-full border-[1.5px] border-current border-t-transparent' />
|
||||
{loadingText}
|
||||
</div>
|
||||
)}
|
||||
</>
|
||||
)
|
||||
}
|
||||
@@ -1,11 +1,9 @@
|
||||
import { useCallback, useEffect, useMemo, useRef, useState } from 'react'
|
||||
import { useCallback, useEffect, useMemo, useState } from 'react'
|
||||
import { useParams } from 'next/navigation'
|
||||
import { Button, Modal, ModalBody, ModalContent, ModalFooter, ModalHeader } from '@/components/emcn'
|
||||
import { Trash } from '@/components/emcn/icons/trash'
|
||||
import { Alert, AlertDescription } from '@/components/ui/alert'
|
||||
import { cn } from '@/lib/core/utils/cn'
|
||||
import { createLogger } from '@/lib/logs/console/logger'
|
||||
import { parseCronToHumanReadable } from '@/lib/workflows/schedules/utils'
|
||||
import { SaveStatusIndicator } from '@/app/workspace/[workspaceId]/w/[workflowId]/components/panel/components/editor/components/sub-block/components/save-status-indicator/save-status-indicator'
|
||||
import { useAutoSave } from '@/hooks/use-auto-save'
|
||||
import { useCollaborativeWorkflow } from '@/hooks/use-collaborative-workflow'
|
||||
import { useScheduleManagement } from '@/hooks/use-schedule-management'
|
||||
import { useSubBlockStore } from '@/stores/workflows/subblock/store'
|
||||
@@ -18,16 +16,9 @@ interface ScheduleSaveProps {
|
||||
disabled?: boolean
|
||||
}
|
||||
|
||||
type SaveStatus = 'idle' | 'saving' | 'saved' | 'error'
|
||||
|
||||
export function ScheduleSave({ blockId, isPreview = false, disabled = false }: ScheduleSaveProps) {
|
||||
const params = useParams()
|
||||
const workflowId = params.workflowId as string
|
||||
const [saveStatus, setSaveStatus] = useState<SaveStatus>('idle')
|
||||
const [errorMessage, setErrorMessage] = useState<string | null>(null)
|
||||
const [deleteStatus, setDeleteStatus] = useState<'idle' | 'deleting'>('idle')
|
||||
const [showDeleteDialog, setShowDeleteDialog] = useState(false)
|
||||
const [scheduleStatus, setScheduleStatus] = useState<'active' | 'disabled' | null>(null)
|
||||
const [nextRunAt, setNextRunAt] = useState<Date | null>(null)
|
||||
const [lastRanAt, setLastRanAt] = useState<Date | null>(null)
|
||||
const [failedCount, setFailedCount] = useState<number>(0)
|
||||
@@ -36,7 +27,7 @@ export function ScheduleSave({ blockId, isPreview = false, disabled = false }: S
|
||||
|
||||
const { collaborativeSetSubblockValue } = useCollaborativeWorkflow()
|
||||
|
||||
const { scheduleId, saveConfig, deleteConfig, isSaving } = useScheduleManagement({
|
||||
const { scheduleId, saveConfig, isSaving } = useScheduleManagement({
|
||||
blockId,
|
||||
isPreview,
|
||||
})
|
||||
@@ -56,13 +47,8 @@ export function ScheduleSave({ blockId, isPreview = false, disabled = false }: S
|
||||
)
|
||||
const scheduleTimezone = useSubBlockStore((state) => state.getValue(blockId, 'timezone'))
|
||||
|
||||
const validateRequiredFields = useCallback((): { valid: boolean; missingFields: string[] } => {
|
||||
const missingFields: string[] = []
|
||||
|
||||
if (!scheduleType) {
|
||||
missingFields.push('Frequency')
|
||||
return { valid: false, missingFields }
|
||||
}
|
||||
const validateRequiredFields = useCallback((): boolean => {
|
||||
if (!scheduleType) return false
|
||||
|
||||
switch (scheduleType) {
|
||||
case 'minutes': {
|
||||
@@ -73,7 +59,7 @@ export function ScheduleSave({ blockId, isPreview = false, disabled = false }: S
|
||||
minutesNum < 1 ||
|
||||
minutesNum > 1440
|
||||
) {
|
||||
missingFields.push('Minutes Interval (must be 1-1440)')
|
||||
return false
|
||||
}
|
||||
break
|
||||
}
|
||||
@@ -87,48 +73,39 @@ export function ScheduleSave({ blockId, isPreview = false, disabled = false }: S
|
||||
hourlyNum < 0 ||
|
||||
hourlyNum > 59
|
||||
) {
|
||||
missingFields.push('Minute (must be 0-59)')
|
||||
return false
|
||||
}
|
||||
break
|
||||
}
|
||||
case 'daily':
|
||||
if (!scheduleDailyTime) {
|
||||
missingFields.push('Time')
|
||||
}
|
||||
if (!scheduleDailyTime) return false
|
||||
break
|
||||
case 'weekly':
|
||||
if (!scheduleWeeklyDay) {
|
||||
missingFields.push('Day of Week')
|
||||
}
|
||||
if (!scheduleWeeklyTime) {
|
||||
missingFields.push('Time')
|
||||
}
|
||||
if (!scheduleWeeklyDay || !scheduleWeeklyTime) return false
|
||||
break
|
||||
case 'monthly': {
|
||||
const monthlyNum = Number(scheduleMonthlyDay)
|
||||
if (!scheduleMonthlyDay || Number.isNaN(monthlyNum) || monthlyNum < 1 || monthlyNum > 31) {
|
||||
missingFields.push('Day of Month (must be 1-31)')
|
||||
}
|
||||
if (!scheduleMonthlyTime) {
|
||||
missingFields.push('Time')
|
||||
if (
|
||||
!scheduleMonthlyDay ||
|
||||
Number.isNaN(monthlyNum) ||
|
||||
monthlyNum < 1 ||
|
||||
monthlyNum > 31 ||
|
||||
!scheduleMonthlyTime
|
||||
) {
|
||||
return false
|
||||
}
|
||||
break
|
||||
}
|
||||
case 'custom':
|
||||
if (!scheduleCronExpression) {
|
||||
missingFields.push('Cron Expression')
|
||||
}
|
||||
if (!scheduleCronExpression) return false
|
||||
break
|
||||
}
|
||||
|
||||
if (!scheduleTimezone && scheduleType !== 'minutes' && scheduleType !== 'hourly') {
|
||||
missingFields.push('Timezone')
|
||||
return false
|
||||
}
|
||||
|
||||
return {
|
||||
valid: missingFields.length === 0,
|
||||
missingFields,
|
||||
}
|
||||
return true
|
||||
}, [
|
||||
scheduleType,
|
||||
scheduleMinutesInterval,
|
||||
@@ -160,7 +137,7 @@ export function ScheduleSave({ blockId, isPreview = false, disabled = false }: S
|
||||
const subscribedSubBlockValues = useSubBlockStore(
|
||||
useCallback(
|
||||
(state) => {
|
||||
const values: Record<string, any> = {}
|
||||
const values: Record<string, unknown> = {}
|
||||
requiredSubBlockIds.forEach((subBlockId) => {
|
||||
const value = state.getValue(blockId, subBlockId)
|
||||
if (value !== null && value !== undefined && value !== '') {
|
||||
@@ -173,52 +150,57 @@ export function ScheduleSave({ blockId, isPreview = false, disabled = false }: S
|
||||
)
|
||||
)
|
||||
|
||||
const previousValuesRef = useRef<Record<string, any>>({})
|
||||
const validationTimeoutRef = useRef<NodeJS.Timeout | null>(null)
|
||||
const configFingerprint = useMemo(() => {
|
||||
return JSON.stringify(subscribedSubBlockValues)
|
||||
}, [subscribedSubBlockValues])
|
||||
|
||||
const handleSaveSuccess = useCallback(
|
||||
async (result: { success: boolean; nextRunAt?: string; cronExpression?: string }) => {
|
||||
const scheduleIdValue = useSubBlockStore.getState().getValue(blockId, 'scheduleId')
|
||||
collaborativeSetSubblockValue(blockId, 'scheduleId', scheduleIdValue)
|
||||
|
||||
if (result.nextRunAt) {
|
||||
setNextRunAt(new Date(result.nextRunAt))
|
||||
}
|
||||
|
||||
await fetchScheduleStatus()
|
||||
|
||||
if (result.cronExpression) {
|
||||
setSavedCronExpression(result.cronExpression)
|
||||
}
|
||||
},
|
||||
[blockId, collaborativeSetSubblockValue]
|
||||
)
|
||||
|
||||
const {
|
||||
saveStatus,
|
||||
errorMessage,
|
||||
retryCount,
|
||||
maxRetries,
|
||||
triggerSave,
|
||||
onConfigChange,
|
||||
markInitialLoadComplete,
|
||||
} = useAutoSave({
|
||||
disabled: isPreview || disabled,
|
||||
isExternallySaving: isSaving,
|
||||
validate: validateRequiredFields,
|
||||
onSave: saveConfig,
|
||||
onSaveSuccess: handleSaveSuccess,
|
||||
loggerName: 'ScheduleSave',
|
||||
})
|
||||
|
||||
useEffect(() => {
|
||||
if (saveStatus !== 'error') {
|
||||
previousValuesRef.current = subscribedSubBlockValues
|
||||
return
|
||||
onConfigChange(configFingerprint)
|
||||
}, [configFingerprint, onConfigChange])
|
||||
|
||||
useEffect(() => {
|
||||
if (!isLoadingStatus && scheduleId) {
|
||||
return markInitialLoadComplete(configFingerprint)
|
||||
}
|
||||
|
||||
const hasChanges = Object.keys(subscribedSubBlockValues).some(
|
||||
(key) =>
|
||||
previousValuesRef.current[key] !== (subscribedSubBlockValues as Record<string, any>)[key]
|
||||
)
|
||||
|
||||
if (!hasChanges) {
|
||||
return
|
||||
if (!scheduleId && !isLoadingStatus) {
|
||||
return markInitialLoadComplete(configFingerprint)
|
||||
}
|
||||
|
||||
if (validationTimeoutRef.current) {
|
||||
clearTimeout(validationTimeoutRef.current)
|
||||
}
|
||||
|
||||
validationTimeoutRef.current = setTimeout(() => {
|
||||
const validation = validateRequiredFields()
|
||||
|
||||
if (validation.valid) {
|
||||
setErrorMessage(null)
|
||||
setSaveStatus('idle')
|
||||
logger.debug('Error cleared after validation passed', { blockId })
|
||||
} else {
|
||||
setErrorMessage(`Missing required fields: ${validation.missingFields.join(', ')}`)
|
||||
logger.debug('Error message updated', {
|
||||
blockId,
|
||||
missingFields: validation.missingFields,
|
||||
})
|
||||
}
|
||||
|
||||
previousValuesRef.current = subscribedSubBlockValues
|
||||
}, 300)
|
||||
|
||||
return () => {
|
||||
if (validationTimeoutRef.current) {
|
||||
clearTimeout(validationTimeoutRef.current)
|
||||
}
|
||||
}
|
||||
}, [blockId, subscribedSubBlockValues, saveStatus, validateRequiredFields])
|
||||
}, [isLoadingStatus, scheduleId, configFingerprint, markInitialLoadComplete])
|
||||
|
||||
const fetchScheduleStatus = useCallback(async () => {
|
||||
if (!scheduleId || isPreview) return
|
||||
@@ -231,7 +213,6 @@ export function ScheduleSave({ blockId, isPreview = false, disabled = false }: S
|
||||
if (response.ok) {
|
||||
const data = await response.json()
|
||||
if (data.schedule) {
|
||||
setScheduleStatus(data.schedule.status)
|
||||
setNextRunAt(data.schedule.nextRunAt ? new Date(data.schedule.nextRunAt) : null)
|
||||
setLastRanAt(data.schedule.lastRanAt ? new Date(data.schedule.lastRanAt) : null)
|
||||
setFailedCount(data.schedule.failedCount || 0)
|
||||
@@ -251,249 +232,82 @@ export function ScheduleSave({ blockId, isPreview = false, disabled = false }: S
|
||||
}
|
||||
}, [scheduleId, isPreview, fetchScheduleStatus])
|
||||
|
||||
const handleSave = async () => {
|
||||
if (isPreview || disabled) return
|
||||
|
||||
setSaveStatus('saving')
|
||||
setErrorMessage(null)
|
||||
|
||||
try {
|
||||
const validation = validateRequiredFields()
|
||||
if (!validation.valid) {
|
||||
setErrorMessage(`Missing required fields: ${validation.missingFields.join(', ')}`)
|
||||
setSaveStatus('error')
|
||||
return
|
||||
}
|
||||
|
||||
const result = await saveConfig()
|
||||
if (!result.success) {
|
||||
throw new Error('Save config returned false')
|
||||
}
|
||||
|
||||
setSaveStatus('saved')
|
||||
setErrorMessage(null)
|
||||
|
||||
const scheduleIdValue = useSubBlockStore.getState().getValue(blockId, 'scheduleId')
|
||||
collaborativeSetSubblockValue(blockId, 'scheduleId', scheduleIdValue)
|
||||
|
||||
if (result.nextRunAt) {
|
||||
setNextRunAt(new Date(result.nextRunAt))
|
||||
setScheduleStatus('active')
|
||||
}
|
||||
|
||||
// Fetch additional status info, then apply cron from save result to prevent stale data
|
||||
await fetchScheduleStatus()
|
||||
|
||||
if (result.cronExpression) {
|
||||
setSavedCronExpression(result.cronExpression)
|
||||
}
|
||||
|
||||
setTimeout(() => {
|
||||
setSaveStatus('idle')
|
||||
}, 2000)
|
||||
|
||||
logger.info('Schedule configuration saved successfully', {
|
||||
blockId,
|
||||
hasScheduleId: !!scheduleId,
|
||||
})
|
||||
} catch (error: any) {
|
||||
setSaveStatus('error')
|
||||
setErrorMessage(error.message || 'An error occurred while saving.')
|
||||
logger.error('Error saving schedule config', { error })
|
||||
}
|
||||
if (isPreview) {
|
||||
return null
|
||||
}
|
||||
|
||||
const handleDelete = async () => {
|
||||
if (isPreview || disabled) return
|
||||
const hasScheduleInfo = scheduleId || isLoadingStatus || saveStatus === 'saving' || errorMessage
|
||||
|
||||
setShowDeleteDialog(false)
|
||||
setDeleteStatus('deleting')
|
||||
|
||||
try {
|
||||
const success = await deleteConfig()
|
||||
if (!success) {
|
||||
throw new Error('Failed to delete schedule')
|
||||
}
|
||||
|
||||
setScheduleStatus(null)
|
||||
setNextRunAt(null)
|
||||
setLastRanAt(null)
|
||||
setFailedCount(0)
|
||||
|
||||
collaborativeSetSubblockValue(blockId, 'scheduleId', null)
|
||||
|
||||
logger.info('Schedule deleted successfully', { blockId })
|
||||
} catch (error: any) {
|
||||
setErrorMessage(error.message || 'An error occurred while deleting.')
|
||||
logger.error('Error deleting schedule', { error })
|
||||
} finally {
|
||||
setDeleteStatus('idle')
|
||||
}
|
||||
}
|
||||
|
||||
const handleDeleteConfirm = () => {
|
||||
handleDelete()
|
||||
}
|
||||
|
||||
const handleToggleStatus = async () => {
|
||||
if (!scheduleId || isPreview || disabled) return
|
||||
|
||||
try {
|
||||
const action = scheduleStatus === 'active' ? 'disable' : 'reactivate'
|
||||
const response = await fetch(`/api/schedules/${scheduleId}`, {
|
||||
method: 'PUT',
|
||||
headers: { 'Content-Type': 'application/json' },
|
||||
body: JSON.stringify({ action }),
|
||||
})
|
||||
|
||||
if (response.ok) {
|
||||
await fetchScheduleStatus()
|
||||
logger.info(`Schedule ${action}d successfully`, { scheduleId })
|
||||
} else {
|
||||
throw new Error(`Failed to ${action} schedule`)
|
||||
}
|
||||
} catch (error: any) {
|
||||
setErrorMessage(
|
||||
error.message ||
|
||||
`An error occurred while ${scheduleStatus === 'active' ? 'disabling' : 'reactivating'} the schedule.`
|
||||
)
|
||||
logger.error('Error toggling schedule status', { error })
|
||||
}
|
||||
if (!hasScheduleInfo) {
|
||||
return null
|
||||
}
|
||||
|
||||
return (
|
||||
<div className='mt-2'>
|
||||
<div className='flex gap-2'>
|
||||
<Button
|
||||
variant='default'
|
||||
onClick={handleSave}
|
||||
disabled={disabled || isPreview || isSaving || saveStatus === 'saving' || isLoadingStatus}
|
||||
className={cn(
|
||||
'h-9 flex-1 rounded-[8px] transition-all duration-200',
|
||||
saveStatus === 'saved' && 'bg-green-600 hover:bg-green-700',
|
||||
saveStatus === 'error' && 'bg-red-600 hover:bg-red-700'
|
||||
)}
|
||||
>
|
||||
{saveStatus === 'saving' && (
|
||||
<>
|
||||
<div className='mr-2 h-4 w-4 animate-spin rounded-full border-[1.5px] border-current border-t-transparent' />
|
||||
Saving...
|
||||
</>
|
||||
)}
|
||||
{saveStatus === 'saved' && 'Saved'}
|
||||
{saveStatus === 'idle' && (scheduleId ? 'Update Schedule' : 'Save Schedule')}
|
||||
{saveStatus === 'error' && 'Error'}
|
||||
</Button>
|
||||
<div className='space-y-1 pb-4'>
|
||||
<SaveStatusIndicator
|
||||
status={saveStatus}
|
||||
errorMessage={errorMessage}
|
||||
savingText='Saving schedule...'
|
||||
loadingText='Loading schedule...'
|
||||
isLoading={isLoadingStatus}
|
||||
onRetry={triggerSave}
|
||||
retryDisabled={isSaving}
|
||||
retryCount={retryCount}
|
||||
maxRetries={maxRetries}
|
||||
/>
|
||||
|
||||
{scheduleId && (
|
||||
<Button
|
||||
variant='default'
|
||||
onClick={() => setShowDeleteDialog(true)}
|
||||
disabled={disabled || isPreview || deleteStatus === 'deleting' || isSaving}
|
||||
className='h-9 rounded-[8px] px-3'
|
||||
>
|
||||
{deleteStatus === 'deleting' ? (
|
||||
<div className='h-4 w-4 animate-spin rounded-full border-[1.5px] border-current border-t-transparent' />
|
||||
) : (
|
||||
<Trash className='h-[14px] w-[14px]' />
|
||||
)}
|
||||
</Button>
|
||||
)}
|
||||
</div>
|
||||
|
||||
{errorMessage && (
|
||||
<Alert variant='destructive' className='mt-2'>
|
||||
<AlertDescription>{errorMessage}</AlertDescription>
|
||||
</Alert>
|
||||
)}
|
||||
|
||||
{scheduleId && (scheduleStatus || isLoadingStatus || nextRunAt) && (
|
||||
<div className='mt-2 space-y-1'>
|
||||
{isLoadingStatus ? (
|
||||
<div className='flex items-center gap-2 text-muted-foreground text-sm'>
|
||||
<div className='h-4 w-4 animate-spin rounded-full border-[1.5px] border-current border-t-transparent' />
|
||||
Loading schedule status...
|
||||
</div>
|
||||
) : (
|
||||
<>
|
||||
{failedCount > 0 && (
|
||||
<div className='flex items-center gap-2'>
|
||||
<span className='text-destructive text-sm'>
|
||||
⚠️ {failedCount} failed run{failedCount !== 1 ? 's' : ''}
|
||||
</span>
|
||||
</div>
|
||||
)}
|
||||
|
||||
{savedCronExpression && (
|
||||
<p className='text-muted-foreground text-sm'>
|
||||
Runs{' '}
|
||||
{parseCronToHumanReadable(
|
||||
savedCronExpression,
|
||||
scheduleTimezone || 'UTC'
|
||||
).toLowerCase()}
|
||||
</p>
|
||||
)}
|
||||
|
||||
{nextRunAt && (
|
||||
<p className='text-sm'>
|
||||
<span className='font-medium'>Next run:</span>{' '}
|
||||
{nextRunAt.toLocaleString('en-US', {
|
||||
timeZone: scheduleTimezone || 'UTC',
|
||||
year: 'numeric',
|
||||
month: 'numeric',
|
||||
day: 'numeric',
|
||||
hour: 'numeric',
|
||||
minute: '2-digit',
|
||||
hour12: true,
|
||||
})}{' '}
|
||||
{scheduleTimezone || 'UTC'}
|
||||
</p>
|
||||
)}
|
||||
|
||||
{lastRanAt && (
|
||||
<p className='text-muted-foreground text-sm'>
|
||||
<span className='font-medium'>Last ran:</span>{' '}
|
||||
{lastRanAt.toLocaleString('en-US', {
|
||||
timeZone: scheduleTimezone || 'UTC',
|
||||
year: 'numeric',
|
||||
month: 'numeric',
|
||||
day: 'numeric',
|
||||
hour: 'numeric',
|
||||
minute: '2-digit',
|
||||
hour12: true,
|
||||
})}{' '}
|
||||
{scheduleTimezone || 'UTC'}
|
||||
</p>
|
||||
)}
|
||||
</>
|
||||
)}
|
||||
</div>
|
||||
)}
|
||||
|
||||
<Modal open={showDeleteDialog} onOpenChange={setShowDeleteDialog}>
|
||||
<ModalContent size='sm'>
|
||||
<ModalHeader>Delete Schedule</ModalHeader>
|
||||
<ModalBody>
|
||||
<p className='text-[12px] text-[var(--text-tertiary)]'>
|
||||
Are you sure you want to delete this schedule configuration? This will stop the
|
||||
workflow from running automatically.{' '}
|
||||
<span className='text-[var(--text-error)]'>This action cannot be undone.</span>
|
||||
{/* Schedule status info */}
|
||||
{scheduleId && !isLoadingStatus && saveStatus !== 'saving' && (
|
||||
<>
|
||||
{failedCount > 0 && (
|
||||
<p className='text-destructive text-sm'>
|
||||
{failedCount} failed run{failedCount !== 1 ? 's' : ''}
|
||||
</p>
|
||||
</ModalBody>
|
||||
<ModalFooter>
|
||||
<Button variant='active' onClick={() => setShowDeleteDialog(false)}>
|
||||
Cancel
|
||||
</Button>
|
||||
<Button
|
||||
variant='primary'
|
||||
onClick={handleDeleteConfirm}
|
||||
className='!bg-[var(--text-error)] !text-white hover:!bg-[var(--text-error)]/90'
|
||||
>
|
||||
Delete
|
||||
</Button>
|
||||
</ModalFooter>
|
||||
</ModalContent>
|
||||
</Modal>
|
||||
)}
|
||||
|
||||
{savedCronExpression && (
|
||||
<p className='text-muted-foreground text-sm'>
|
||||
Runs{' '}
|
||||
{parseCronToHumanReadable(
|
||||
savedCronExpression,
|
||||
scheduleTimezone || 'UTC'
|
||||
).toLowerCase()}
|
||||
</p>
|
||||
)}
|
||||
|
||||
{nextRunAt && (
|
||||
<p className='text-sm'>
|
||||
<span className='font-medium'>Next run:</span>{' '}
|
||||
{nextRunAt.toLocaleString('en-US', {
|
||||
timeZone: scheduleTimezone || 'UTC',
|
||||
year: 'numeric',
|
||||
month: 'numeric',
|
||||
day: 'numeric',
|
||||
hour: 'numeric',
|
||||
minute: '2-digit',
|
||||
hour12: true,
|
||||
})}{' '}
|
||||
{scheduleTimezone || 'UTC'}
|
||||
</p>
|
||||
)}
|
||||
|
||||
{lastRanAt && (
|
||||
<p className='text-muted-foreground text-sm'>
|
||||
<span className='font-medium'>Last ran:</span>{' '}
|
||||
{lastRanAt.toLocaleString('en-US', {
|
||||
timeZone: scheduleTimezone || 'UTC',
|
||||
year: 'numeric',
|
||||
month: 'numeric',
|
||||
day: 'numeric',
|
||||
hour: 'numeric',
|
||||
minute: '2-digit',
|
||||
hour12: true,
|
||||
})}{' '}
|
||||
{scheduleTimezone || 'UTC'}
|
||||
</p>
|
||||
)}
|
||||
</>
|
||||
)}
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
@@ -1,23 +1,15 @@
|
||||
import { useCallback, useEffect, useMemo, useRef, useState } from 'react'
|
||||
import {
|
||||
Button,
|
||||
Modal,
|
||||
ModalBody,
|
||||
ModalContent,
|
||||
ModalFooter,
|
||||
ModalHeader,
|
||||
} from '@/components/emcn/components'
|
||||
import { Trash } from '@/components/emcn/icons/trash'
|
||||
import { Alert, AlertDescription } from '@/components/ui/alert'
|
||||
import { cn } from '@/lib/core/utils/cn'
|
||||
import { useCallback, useEffect, useMemo, useState } from 'react'
|
||||
import { Button } from '@/components/emcn/components'
|
||||
import { createLogger } from '@/lib/logs/console/logger'
|
||||
import { SaveStatusIndicator } from '@/app/workspace/[workspaceId]/w/[workflowId]/components/panel/components/editor/components/sub-block/components/save-status-indicator/save-status-indicator'
|
||||
import { ShortInput } from '@/app/workspace/[workspaceId]/w/[workflowId]/components/panel/components/editor/components/sub-block/components/short-input/short-input'
|
||||
import { useAutoSave } from '@/hooks/use-auto-save'
|
||||
import { useCollaborativeWorkflow } from '@/hooks/use-collaborative-workflow'
|
||||
import { useTriggerConfigAggregation } from '@/hooks/use-trigger-config-aggregation'
|
||||
import { getTriggerConfigAggregation } from '@/hooks/use-trigger-config-aggregation'
|
||||
import { useWebhookManagement } from '@/hooks/use-webhook-management'
|
||||
import { useSubBlockStore } from '@/stores/workflows/subblock/store'
|
||||
import { getTrigger, isTriggerValid } from '@/triggers'
|
||||
import { SYSTEM_SUBBLOCK_IDS } from '@/triggers/constants'
|
||||
import { ShortInput } from '../short-input/short-input'
|
||||
|
||||
const logger = createLogger('TriggerSave')
|
||||
|
||||
@@ -29,8 +21,6 @@ interface TriggerSaveProps {
|
||||
disabled?: boolean
|
||||
}
|
||||
|
||||
type SaveStatus = 'idle' | 'saving' | 'saved' | 'error'
|
||||
|
||||
export function TriggerSave({
|
||||
blockId,
|
||||
subBlockId,
|
||||
@@ -38,11 +28,8 @@ export function TriggerSave({
|
||||
isPreview = false,
|
||||
disabled = false,
|
||||
}: TriggerSaveProps) {
|
||||
const [saveStatus, setSaveStatus] = useState<SaveStatus>('idle')
|
||||
const [errorMessage, setErrorMessage] = useState<string | null>(null)
|
||||
const [deleteStatus, setDeleteStatus] = useState<'idle' | 'deleting'>('idle')
|
||||
const [showDeleteDialog, setShowDeleteDialog] = useState(false)
|
||||
const [isGeneratingTestUrl, setIsGeneratingTestUrl] = useState(false)
|
||||
const [testUrlError, setTestUrlError] = useState<string | null>(null)
|
||||
|
||||
const storedTestUrl = useSubBlockStore((state) => state.getValue(blockId, 'testUrl'))
|
||||
const storedTestUrlExpiresAt = useSubBlockStore((state) =>
|
||||
@@ -70,13 +57,12 @@ export function TriggerSave({
|
||||
|
||||
const { collaborativeSetSubblockValue } = useCollaborativeWorkflow()
|
||||
|
||||
const { webhookId, saveConfig, deleteConfig, isLoading } = useWebhookManagement({
|
||||
const { webhookId, saveConfig, isLoading } = useWebhookManagement({
|
||||
blockId,
|
||||
triggerId: effectiveTriggerId,
|
||||
isPreview,
|
||||
})
|
||||
|
||||
const triggerConfig = useSubBlockStore((state) => state.getValue(blockId, 'triggerConfig'))
|
||||
const triggerCredentials = useSubBlockStore((state) =>
|
||||
state.getValue(blockId, 'triggerCredentials')
|
||||
)
|
||||
@@ -87,40 +73,26 @@ export function TriggerSave({
|
||||
const hasWebhookUrlDisplay =
|
||||
triggerDef?.subBlocks.some((sb) => sb.id === 'webhookUrlDisplay') ?? false
|
||||
|
||||
const validateRequiredFields = useCallback(
|
||||
(
|
||||
configToCheck: Record<string, any> | null | undefined
|
||||
): { valid: boolean; missingFields: string[] } => {
|
||||
if (!triggerDef) {
|
||||
return { valid: true, missingFields: [] }
|
||||
const validateRequiredFields = useCallback((): boolean => {
|
||||
if (!triggerDef) return true
|
||||
|
||||
const aggregatedConfig = getTriggerConfigAggregation(blockId, effectiveTriggerId)
|
||||
|
||||
const requiredSubBlocks = triggerDef.subBlocks.filter(
|
||||
(sb) => sb.required && sb.mode === 'trigger' && !SYSTEM_SUBBLOCK_IDS.includes(sb.id)
|
||||
)
|
||||
|
||||
for (const subBlock of requiredSubBlocks) {
|
||||
if (subBlock.id === 'triggerCredentials') {
|
||||
if (!triggerCredentials) return false
|
||||
} else {
|
||||
const value = aggregatedConfig?.[subBlock.id]
|
||||
if (value === undefined || value === null || value === '') return false
|
||||
}
|
||||
}
|
||||
|
||||
const missingFields: string[] = []
|
||||
|
||||
triggerDef.subBlocks
|
||||
.filter(
|
||||
(sb) => sb.required && sb.mode === 'trigger' && !SYSTEM_SUBBLOCK_IDS.includes(sb.id)
|
||||
)
|
||||
.forEach((subBlock) => {
|
||||
if (subBlock.id === 'triggerCredentials') {
|
||||
if (!triggerCredentials) {
|
||||
missingFields.push(subBlock.title || 'Credentials')
|
||||
}
|
||||
} else {
|
||||
const value = configToCheck?.[subBlock.id]
|
||||
if (value === undefined || value === null || value === '') {
|
||||
missingFields.push(subBlock.title || subBlock.id)
|
||||
}
|
||||
}
|
||||
})
|
||||
|
||||
return {
|
||||
valid: missingFields.length === 0,
|
||||
missingFields,
|
||||
}
|
||||
},
|
||||
[triggerDef, triggerCredentials]
|
||||
)
|
||||
return true
|
||||
}, [triggerDef, triggerCredentials, blockId, effectiveTriggerId])
|
||||
|
||||
const requiredSubBlockIds = useMemo(() => {
|
||||
if (!triggerDef) return []
|
||||
@@ -133,11 +105,11 @@ export function TriggerSave({
|
||||
useCallback(
|
||||
(state) => {
|
||||
if (!triggerDef) return {}
|
||||
const values: Record<string, any> = {}
|
||||
requiredSubBlockIds.forEach((subBlockId) => {
|
||||
const value = state.getValue(blockId, subBlockId)
|
||||
const values: Record<string, unknown> = {}
|
||||
requiredSubBlockIds.forEach((id) => {
|
||||
const value = state.getValue(blockId, id)
|
||||
if (value !== null && value !== undefined && value !== '') {
|
||||
values[subBlockId] = value
|
||||
values[id] = value
|
||||
}
|
||||
})
|
||||
return values
|
||||
@@ -146,69 +118,9 @@ export function TriggerSave({
|
||||
)
|
||||
)
|
||||
|
||||
const previousValuesRef = useRef<Record<string, any>>({})
|
||||
const validationTimeoutRef = useRef<NodeJS.Timeout | null>(null)
|
||||
|
||||
useEffect(() => {
|
||||
if (saveStatus !== 'error' || !triggerDef) {
|
||||
previousValuesRef.current = subscribedSubBlockValues
|
||||
return
|
||||
}
|
||||
|
||||
const hasChanges = Object.keys(subscribedSubBlockValues).some(
|
||||
(key) =>
|
||||
previousValuesRef.current[key] !== (subscribedSubBlockValues as Record<string, any>)[key]
|
||||
)
|
||||
|
||||
if (!hasChanges) {
|
||||
return
|
||||
}
|
||||
|
||||
if (validationTimeoutRef.current) {
|
||||
clearTimeout(validationTimeoutRef.current)
|
||||
}
|
||||
|
||||
validationTimeoutRef.current = setTimeout(() => {
|
||||
const aggregatedConfig = useTriggerConfigAggregation(blockId, effectiveTriggerId)
|
||||
|
||||
if (aggregatedConfig) {
|
||||
useSubBlockStore.getState().setValue(blockId, 'triggerConfig', aggregatedConfig)
|
||||
}
|
||||
|
||||
const validation = validateRequiredFields(aggregatedConfig)
|
||||
|
||||
if (validation.valid) {
|
||||
setErrorMessage(null)
|
||||
setSaveStatus('idle')
|
||||
logger.debug('Error cleared after validation passed', {
|
||||
blockId,
|
||||
triggerId: effectiveTriggerId,
|
||||
})
|
||||
} else {
|
||||
setErrorMessage(`Missing required fields: ${validation.missingFields.join(', ')}`)
|
||||
logger.debug('Error message updated', {
|
||||
blockId,
|
||||
triggerId: effectiveTriggerId,
|
||||
missingFields: validation.missingFields,
|
||||
})
|
||||
}
|
||||
|
||||
previousValuesRef.current = subscribedSubBlockValues
|
||||
}, 300)
|
||||
|
||||
return () => {
|
||||
if (validationTimeoutRef.current) {
|
||||
clearTimeout(validationTimeoutRef.current)
|
||||
}
|
||||
}
|
||||
}, [
|
||||
blockId,
|
||||
effectiveTriggerId,
|
||||
triggerDef,
|
||||
subscribedSubBlockValues,
|
||||
saveStatus,
|
||||
validateRequiredFields,
|
||||
])
|
||||
const configFingerprint = useMemo(() => {
|
||||
return JSON.stringify({ ...subscribedSubBlockValues, triggerCredentials })
|
||||
}, [subscribedSubBlockValues, triggerCredentials])
|
||||
|
||||
useEffect(() => {
|
||||
if (isTestUrlExpired && storedTestUrl) {
|
||||
@@ -217,69 +129,63 @@ export function TriggerSave({
|
||||
}
|
||||
}, [blockId, isTestUrlExpired, storedTestUrl])
|
||||
|
||||
const handleSave = async () => {
|
||||
if (isPreview || disabled) return
|
||||
const handleSave = useCallback(async () => {
|
||||
const aggregatedConfig = getTriggerConfigAggregation(blockId, effectiveTriggerId)
|
||||
|
||||
setSaveStatus('saving')
|
||||
setErrorMessage(null)
|
||||
|
||||
try {
|
||||
const aggregatedConfig = useTriggerConfigAggregation(blockId, effectiveTriggerId)
|
||||
|
||||
if (aggregatedConfig) {
|
||||
useSubBlockStore.getState().setValue(blockId, 'triggerConfig', aggregatedConfig)
|
||||
logger.debug('Stored aggregated trigger config', {
|
||||
blockId,
|
||||
triggerId: effectiveTriggerId,
|
||||
aggregatedConfig,
|
||||
})
|
||||
}
|
||||
|
||||
const validation = validateRequiredFields(aggregatedConfig)
|
||||
if (!validation.valid) {
|
||||
setErrorMessage(`Missing required fields: ${validation.missingFields.join(', ')}`)
|
||||
setSaveStatus('error')
|
||||
return
|
||||
}
|
||||
|
||||
const success = await saveConfig()
|
||||
if (!success) {
|
||||
throw new Error('Save config returned false')
|
||||
}
|
||||
|
||||
setSaveStatus('saved')
|
||||
setErrorMessage(null)
|
||||
|
||||
const savedWebhookId = useSubBlockStore.getState().getValue(blockId, 'webhookId')
|
||||
const savedTriggerPath = useSubBlockStore.getState().getValue(blockId, 'triggerPath')
|
||||
const savedTriggerId = useSubBlockStore.getState().getValue(blockId, 'triggerId')
|
||||
const savedTriggerConfig = useSubBlockStore.getState().getValue(blockId, 'triggerConfig')
|
||||
|
||||
collaborativeSetSubblockValue(blockId, 'webhookId', savedWebhookId)
|
||||
collaborativeSetSubblockValue(blockId, 'triggerPath', savedTriggerPath)
|
||||
collaborativeSetSubblockValue(blockId, 'triggerId', savedTriggerId)
|
||||
collaborativeSetSubblockValue(blockId, 'triggerConfig', savedTriggerConfig)
|
||||
|
||||
setTimeout(() => {
|
||||
setSaveStatus('idle')
|
||||
}, 2000)
|
||||
|
||||
logger.info('Trigger configuration saved successfully', {
|
||||
blockId,
|
||||
triggerId: effectiveTriggerId,
|
||||
hasWebhookId: !!webhookId,
|
||||
})
|
||||
} catch (error: any) {
|
||||
setSaveStatus('error')
|
||||
setErrorMessage(error.message || 'An error occurred while saving.')
|
||||
logger.error('Error saving trigger configuration', { error })
|
||||
if (aggregatedConfig) {
|
||||
useSubBlockStore.getState().setValue(blockId, 'triggerConfig', aggregatedConfig)
|
||||
}
|
||||
}
|
||||
|
||||
return saveConfig()
|
||||
}, [blockId, effectiveTriggerId, saveConfig])
|
||||
|
||||
const handleSaveSuccess = useCallback(() => {
|
||||
const savedWebhookId = useSubBlockStore.getState().getValue(blockId, 'webhookId')
|
||||
const savedTriggerPath = useSubBlockStore.getState().getValue(blockId, 'triggerPath')
|
||||
const savedTriggerId = useSubBlockStore.getState().getValue(blockId, 'triggerId')
|
||||
const savedTriggerConfig = useSubBlockStore.getState().getValue(blockId, 'triggerConfig')
|
||||
|
||||
collaborativeSetSubblockValue(blockId, 'webhookId', savedWebhookId)
|
||||
collaborativeSetSubblockValue(blockId, 'triggerPath', savedTriggerPath)
|
||||
collaborativeSetSubblockValue(blockId, 'triggerId', savedTriggerId)
|
||||
collaborativeSetSubblockValue(blockId, 'triggerConfig', savedTriggerConfig)
|
||||
}, [blockId, collaborativeSetSubblockValue])
|
||||
|
||||
const {
|
||||
saveStatus,
|
||||
errorMessage,
|
||||
retryCount,
|
||||
maxRetries,
|
||||
triggerSave,
|
||||
onConfigChange,
|
||||
markInitialLoadComplete,
|
||||
} = useAutoSave({
|
||||
disabled: isPreview || disabled || !triggerDef,
|
||||
isExternallySaving: isLoading,
|
||||
validate: validateRequiredFields,
|
||||
onSave: handleSave,
|
||||
onSaveSuccess: handleSaveSuccess,
|
||||
loggerName: 'TriggerSave',
|
||||
})
|
||||
|
||||
useEffect(() => {
|
||||
onConfigChange(configFingerprint)
|
||||
}, [configFingerprint, onConfigChange])
|
||||
|
||||
useEffect(() => {
|
||||
if (!isLoading && webhookId) {
|
||||
return markInitialLoadComplete(configFingerprint)
|
||||
}
|
||||
if (!webhookId && !isLoading) {
|
||||
return markInitialLoadComplete(configFingerprint)
|
||||
}
|
||||
}, [isLoading, webhookId, configFingerprint, markInitialLoadComplete])
|
||||
|
||||
const generateTestUrl = async () => {
|
||||
if (!webhookId) return
|
||||
try {
|
||||
setIsGeneratingTestUrl(true)
|
||||
setTestUrlError(null)
|
||||
const res = await fetch(`/api/webhooks/${webhookId}/test-url`, {
|
||||
method: 'POST',
|
||||
headers: { 'Content-Type': 'application/json' },
|
||||
@@ -296,7 +202,7 @@ export function TriggerSave({
|
||||
collaborativeSetSubblockValue(blockId, 'testUrlExpiresAt', json.expiresAt)
|
||||
} catch (e) {
|
||||
logger.error('Failed to generate test webhook URL', { error: e })
|
||||
setErrorMessage(
|
||||
setTestUrlError(
|
||||
e instanceof Error ? e.message : 'Failed to generate test URL. Please try again.'
|
||||
)
|
||||
} finally {
|
||||
@@ -304,114 +210,49 @@ export function TriggerSave({
|
||||
}
|
||||
}
|
||||
|
||||
const handleDeleteClick = () => {
|
||||
if (isPreview || disabled || !webhookId) return
|
||||
setShowDeleteDialog(true)
|
||||
}
|
||||
|
||||
const handleDeleteConfirm = async () => {
|
||||
setShowDeleteDialog(false)
|
||||
setDeleteStatus('deleting')
|
||||
setErrorMessage(null)
|
||||
|
||||
try {
|
||||
const success = await deleteConfig()
|
||||
|
||||
if (success) {
|
||||
setDeleteStatus('idle')
|
||||
setSaveStatus('idle')
|
||||
setErrorMessage(null)
|
||||
|
||||
useSubBlockStore.getState().setValue(blockId, 'testUrl', null)
|
||||
useSubBlockStore.getState().setValue(blockId, 'testUrlExpiresAt', null)
|
||||
|
||||
collaborativeSetSubblockValue(blockId, 'triggerPath', '')
|
||||
collaborativeSetSubblockValue(blockId, 'webhookId', null)
|
||||
collaborativeSetSubblockValue(blockId, 'triggerConfig', null)
|
||||
collaborativeSetSubblockValue(blockId, 'testUrl', null)
|
||||
collaborativeSetSubblockValue(blockId, 'testUrlExpiresAt', null)
|
||||
|
||||
logger.info('Trigger configuration deleted successfully', {
|
||||
blockId,
|
||||
triggerId: effectiveTriggerId,
|
||||
})
|
||||
} else {
|
||||
setDeleteStatus('idle')
|
||||
setErrorMessage('Failed to delete trigger configuration.')
|
||||
logger.error('Failed to delete trigger configuration')
|
||||
}
|
||||
} catch (error: any) {
|
||||
setDeleteStatus('idle')
|
||||
setErrorMessage(error.message || 'An error occurred while deleting.')
|
||||
logger.error('Error deleting trigger configuration', { error })
|
||||
}
|
||||
}
|
||||
|
||||
if (isPreview) {
|
||||
return null
|
||||
}
|
||||
|
||||
const isProcessing = saveStatus === 'saving' || deleteStatus === 'deleting' || isLoading
|
||||
const isProcessing = saveStatus === 'saving' || isLoading
|
||||
const displayError = errorMessage || testUrlError
|
||||
|
||||
const hasStatusIndicator = isLoading || saveStatus === 'saving' || displayError
|
||||
const hasTestUrlSection =
|
||||
webhookId && hasWebhookUrlDisplay && !isLoading && saveStatus !== 'saving'
|
||||
|
||||
if (!hasStatusIndicator && !hasTestUrlSection) {
|
||||
return null
|
||||
}
|
||||
|
||||
return (
|
||||
<div id={`${blockId}-${subBlockId}`}>
|
||||
<div className='flex gap-2'>
|
||||
<Button
|
||||
variant='default'
|
||||
onClick={handleSave}
|
||||
disabled={disabled || isProcessing}
|
||||
className={cn(
|
||||
'h-[32px] flex-1 rounded-[8px] px-[12px] transition-all duration-200',
|
||||
saveStatus === 'saved' && 'bg-green-600 hover:bg-green-700',
|
||||
saveStatus === 'error' && 'bg-red-600 hover:bg-red-700'
|
||||
)}
|
||||
>
|
||||
{saveStatus === 'saving' && (
|
||||
<>
|
||||
<div className='mr-2 h-4 w-4 animate-spin rounded-full border-[1.5px] border-current border-t-transparent' />
|
||||
Saving...
|
||||
</>
|
||||
)}
|
||||
{saveStatus === 'saved' && 'Saved'}
|
||||
{saveStatus === 'error' && 'Error'}
|
||||
{saveStatus === 'idle' && (webhookId ? 'Update Configuration' : 'Save Configuration')}
|
||||
</Button>
|
||||
<div id={`${blockId}-${subBlockId}`} className='space-y-2 pb-4'>
|
||||
<SaveStatusIndicator
|
||||
status={saveStatus}
|
||||
errorMessage={displayError}
|
||||
savingText='Saving trigger...'
|
||||
loadingText='Loading trigger...'
|
||||
isLoading={isLoading}
|
||||
onRetry={testUrlError ? () => setTestUrlError(null) : triggerSave}
|
||||
retryDisabled={isProcessing}
|
||||
retryCount={retryCount}
|
||||
maxRetries={maxRetries}
|
||||
/>
|
||||
|
||||
{webhookId && (
|
||||
<Button
|
||||
variant='default'
|
||||
onClick={handleDeleteClick}
|
||||
disabled={disabled || isProcessing}
|
||||
className='h-[32px] rounded-[8px] px-[12px]'
|
||||
>
|
||||
{deleteStatus === 'deleting' ? (
|
||||
<div className='h-4 w-4 animate-spin rounded-full border-[1.5px] border-current border-t-transparent' />
|
||||
) : (
|
||||
<Trash className='h-[14px] w-[14px]' />
|
||||
)}
|
||||
</Button>
|
||||
)}
|
||||
</div>
|
||||
|
||||
{errorMessage && (
|
||||
<Alert variant='destructive' className='mt-2'>
|
||||
<AlertDescription>{errorMessage}</AlertDescription>
|
||||
</Alert>
|
||||
)}
|
||||
|
||||
{webhookId && hasWebhookUrlDisplay && (
|
||||
<div className='mt-2 space-y-1'>
|
||||
{/* Test webhook URL section */}
|
||||
{webhookId && hasWebhookUrlDisplay && !isLoading && saveStatus !== 'saving' && (
|
||||
<div className='space-y-1'>
|
||||
<div className='flex items-center justify-between'>
|
||||
<span className='font-medium text-sm'>Test Webhook URL</span>
|
||||
<Button
|
||||
variant='outline'
|
||||
variant='ghost'
|
||||
onClick={generateTestUrl}
|
||||
disabled={isGeneratingTestUrl || isProcessing}
|
||||
className='h-[32px] rounded-[8px] px-[12px]'
|
||||
className='h-6 px-2 py-1 text-[11px]'
|
||||
>
|
||||
{isGeneratingTestUrl ? (
|
||||
<>
|
||||
<div className='mr-2 h-3 w-3 animate-spin rounded-full border-[1.5px] border-current border-t-transparent' />
|
||||
<div className='mr-1.5 h-2.5 w-2.5 animate-spin rounded-full border-[1.5px] border-current border-t-transparent' />
|
||||
Generating…
|
||||
</>
|
||||
) : testUrl ? (
|
||||
@@ -450,31 +291,6 @@ export function TriggerSave({
|
||||
)}
|
||||
</div>
|
||||
)}
|
||||
|
||||
<Modal open={showDeleteDialog} onOpenChange={setShowDeleteDialog}>
|
||||
<ModalContent size='sm'>
|
||||
<ModalHeader>Delete Trigger</ModalHeader>
|
||||
<ModalBody>
|
||||
<p className='text-[12px] text-[var(--text-tertiary)]'>
|
||||
Are you sure you want to delete this trigger configuration? This will remove the
|
||||
webhook and stop all incoming triggers.{' '}
|
||||
<span className='text-[var(--text-error)]'>This action cannot be undone.</span>
|
||||
</p>
|
||||
</ModalBody>
|
||||
<ModalFooter>
|
||||
<Button variant='active' onClick={() => setShowDeleteDialog(false)}>
|
||||
Cancel
|
||||
</Button>
|
||||
<Button
|
||||
variant='primary'
|
||||
onClick={handleDeleteConfirm}
|
||||
className='!bg-[var(--text-error)] !text-white hover:!bg-[var(--text-error)]/90'
|
||||
>
|
||||
Delete
|
||||
</Button>
|
||||
</ModalFooter>
|
||||
</ModalContent>
|
||||
</Modal>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
@@ -293,7 +293,6 @@ function SubBlockComponent({
|
||||
setIsValidJson(isValid)
|
||||
}
|
||||
|
||||
// Check if wand is enabled for this sub-block
|
||||
const isWandEnabled = config.wandConfig?.enabled ?? false
|
||||
|
||||
/**
|
||||
@@ -816,6 +815,13 @@ function SubBlockComponent({
|
||||
}
|
||||
}
|
||||
|
||||
// Render without wrapper for components that may return null
|
||||
const noWrapper =
|
||||
config.noWrapper || config.type === 'trigger-save' || config.type === 'schedule-save'
|
||||
if (noWrapper) {
|
||||
return renderInput()
|
||||
}
|
||||
|
||||
return (
|
||||
<div onMouseDown={handleMouseDown} className='flex flex-col gap-[10px]'>
|
||||
{renderLabel(
|
||||
|
||||
@@ -336,6 +336,26 @@ export function Editor() {
|
||||
subBlockState
|
||||
)
|
||||
|
||||
const isNoWrapper =
|
||||
subBlock.noWrapper ||
|
||||
subBlock.type === 'trigger-save' ||
|
||||
subBlock.type === 'schedule-save'
|
||||
|
||||
if (isNoWrapper) {
|
||||
return (
|
||||
<SubBlock
|
||||
key={stableKey}
|
||||
blockId={currentBlockId}
|
||||
config={subBlock}
|
||||
isPreview={false}
|
||||
subBlockValues={subBlockState}
|
||||
disabled={!userPermissions.canEdit}
|
||||
fieldDiffStatus={undefined}
|
||||
allowExpandInPreview={false}
|
||||
/>
|
||||
)
|
||||
}
|
||||
|
||||
return (
|
||||
<div key={stableKey}>
|
||||
<SubBlock
|
||||
|
||||
@@ -1,6 +1,8 @@
|
||||
import { useCallback, useEffect, useState } from 'react'
|
||||
import { createLogger } from '@/lib/logs/console/logger'
|
||||
import { parseCronToHumanReadable } from '@/lib/workflows/schedules/utils'
|
||||
import { useWorkflowRegistry } from '@/stores/workflows/registry/store'
|
||||
import { useSubBlockStore } from '@/stores/workflows/subblock/store'
|
||||
import type { ScheduleInfo } from '../types'
|
||||
|
||||
const logger = createLogger('useScheduleInfo')
|
||||
@@ -32,9 +34,20 @@ export function useScheduleInfo(
|
||||
blockType: string,
|
||||
workflowId: string
|
||||
): UseScheduleInfoReturn {
|
||||
const activeWorkflowId = useWorkflowRegistry((state) => state.activeWorkflowId)
|
||||
const [isLoading, setIsLoading] = useState(false)
|
||||
const [scheduleInfo, setScheduleInfo] = useState<ScheduleInfo | null>(null)
|
||||
|
||||
const scheduleIdFromStore = useSubBlockStore(
|
||||
useCallback(
|
||||
(state) => {
|
||||
if (!activeWorkflowId) return null
|
||||
return state.workflowValues[activeWorkflowId]?.[blockId]?.scheduleId as string | null
|
||||
},
|
||||
[activeWorkflowId, blockId]
|
||||
)
|
||||
)
|
||||
|
||||
const fetchScheduleInfo = useCallback(
|
||||
async (wfId: string) => {
|
||||
if (!wfId) return
|
||||
@@ -143,22 +156,10 @@ export function useScheduleInfo(
|
||||
setIsLoading(false)
|
||||
}
|
||||
|
||||
const handleScheduleUpdate = (event: CustomEvent) => {
|
||||
if (event.detail?.workflowId === workflowId && event.detail?.blockId === blockId) {
|
||||
logger.debug('Schedule update event received, refetching schedule info')
|
||||
if (blockType === 'schedule') {
|
||||
fetchScheduleInfo(workflowId)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
window.addEventListener('schedule-updated', handleScheduleUpdate as EventListener)
|
||||
|
||||
return () => {
|
||||
setIsLoading(false)
|
||||
window.removeEventListener('schedule-updated', handleScheduleUpdate as EventListener)
|
||||
}
|
||||
}, [blockType, workflowId, blockId, fetchScheduleInfo])
|
||||
}, [blockType, workflowId, blockId, scheduleIdFromStore, fetchScheduleInfo])
|
||||
|
||||
return {
|
||||
scheduleInfo,
|
||||
|
||||
@@ -44,7 +44,11 @@ export function useWebhookInfo(blockId: string, workflowId: string): UseWebhookI
|
||||
useCallback(
|
||||
(state) => {
|
||||
const blockValues = state.workflowValues[activeWorkflowId || '']?.[blockId]
|
||||
return !!(blockValues?.webhookProvider && blockValues?.webhookPath)
|
||||
// Check for webhookId (set by trigger auto-save) or webhookProvider+webhookPath (legacy)
|
||||
return !!(
|
||||
blockValues?.webhookId ||
|
||||
(blockValues?.webhookProvider && blockValues?.webhookPath)
|
||||
)
|
||||
},
|
||||
[activeWorkflowId, blockId]
|
||||
)
|
||||
@@ -72,6 +76,16 @@ export function useWebhookInfo(blockId: string, workflowId: string): UseWebhookI
|
||||
)
|
||||
)
|
||||
|
||||
const webhookIdFromStore = useSubBlockStore(
|
||||
useCallback(
|
||||
(state) => {
|
||||
if (!activeWorkflowId) return null
|
||||
return state.workflowValues[activeWorkflowId]?.[blockId]?.webhookId as string | null
|
||||
},
|
||||
[activeWorkflowId, blockId]
|
||||
)
|
||||
)
|
||||
|
||||
const fetchWebhookStatus = useCallback(async () => {
|
||||
if (!workflowId || !blockId || !isWebhookConfigured) {
|
||||
setWebhookStatus({ isDisabled: false, webhookId: undefined })
|
||||
@@ -114,7 +128,7 @@ export function useWebhookInfo(blockId: string, workflowId: string): UseWebhookI
|
||||
|
||||
useEffect(() => {
|
||||
fetchWebhookStatus()
|
||||
}, [fetchWebhookStatus])
|
||||
}, [fetchWebhookStatus, webhookIdFromStore])
|
||||
|
||||
const reactivateWebhook = useCallback(
|
||||
async (webhookId: string) => {
|
||||
|
||||
@@ -550,9 +550,6 @@ export const WorkflowBlock = memo(function WorkflowBlock({
|
||||
|
||||
const currentStoreBlock = currentWorkflow.getBlockById(id)
|
||||
|
||||
const isStarterBlock = type === 'starter'
|
||||
const isWebhookTriggerBlock = type === 'webhook' || type === 'generic_webhook'
|
||||
|
||||
/**
|
||||
* Subscribe to this block's subblock values to track changes for conditional rendering
|
||||
* of subblocks based on their conditions.
|
||||
@@ -808,7 +805,7 @@ export const WorkflowBlock = memo(function WorkflowBlock({
|
||||
updateNodeInternals(id)
|
||||
}, [horizontalHandles, id, updateNodeInternals])
|
||||
|
||||
const showWebhookIndicator = (isStarterBlock || isWebhookTriggerBlock) && isWebhookConfigured
|
||||
const showWebhookIndicator = displayTriggerMode && isWebhookConfigured
|
||||
const shouldShowScheduleBadge =
|
||||
type === 'schedule' && !isLoadingScheduleInfo && scheduleInfo !== null
|
||||
const userPermissions = useUserPermissionsContext()
|
||||
@@ -909,28 +906,30 @@ export const WorkflowBlock = memo(function WorkflowBlock({
|
||||
)}
|
||||
{!isEnabled && <Badge>disabled</Badge>}
|
||||
|
||||
{type === 'schedule' && shouldShowScheduleBadge && scheduleInfo?.isDisabled && (
|
||||
{type === 'schedule' && shouldShowScheduleBadge && (
|
||||
<Tooltip.Root>
|
||||
<Tooltip.Trigger asChild>
|
||||
<Badge
|
||||
variant='outline'
|
||||
className='cursor-pointer'
|
||||
className={scheduleInfo?.isDisabled ? 'cursor-pointer' : ''}
|
||||
style={{
|
||||
borderColor: 'var(--warning)',
|
||||
color: 'var(--warning)',
|
||||
borderColor: scheduleInfo?.isDisabled ? 'var(--warning)' : 'var(--success)',
|
||||
color: scheduleInfo?.isDisabled ? 'var(--warning)' : 'var(--success)',
|
||||
}}
|
||||
onClick={(e) => {
|
||||
e.stopPropagation()
|
||||
if (scheduleInfo?.id) {
|
||||
if (scheduleInfo?.isDisabled && scheduleInfo?.id) {
|
||||
reactivateSchedule(scheduleInfo.id)
|
||||
}
|
||||
}}
|
||||
>
|
||||
disabled
|
||||
{scheduleInfo?.isDisabled ? 'disabled' : 'active'}
|
||||
</Badge>
|
||||
</Tooltip.Trigger>
|
||||
<Tooltip.Content>
|
||||
<span className='text-sm'>Click to reactivate</span>
|
||||
{scheduleInfo?.isDisabled
|
||||
? 'Click to reactivate'
|
||||
: scheduleInfo?.scheduleTiming || 'Schedule is active'}
|
||||
</Tooltip.Content>
|
||||
</Tooltip.Root>
|
||||
)}
|
||||
@@ -940,47 +939,27 @@ export const WorkflowBlock = memo(function WorkflowBlock({
|
||||
<Tooltip.Trigger asChild>
|
||||
<Badge
|
||||
variant='outline'
|
||||
className='bg-[var(--brand-tertiary)] text-[var(--brand-tertiary)]'
|
||||
>
|
||||
<div className='relative flex items-center justify-center'>
|
||||
<div className='197, 94, 0.2)] absolute h-3 w-3 rounded-full bg-[rgba(34,' />
|
||||
<div className='relative h-2 w-2 rounded-full bg-[var(--brand-tertiary)]' />
|
||||
</div>
|
||||
Webhook
|
||||
</Badge>
|
||||
</Tooltip.Trigger>
|
||||
<Tooltip.Content side='top' className='max-w-[300px] p-4'>
|
||||
{webhookProvider && webhookPath ? (
|
||||
<>
|
||||
<p className='text-sm'>{getProviderName(webhookProvider)} Webhook</p>
|
||||
<p className='mt-1 text-muted-foreground text-xs'>Path: {webhookPath}</p>
|
||||
</>
|
||||
) : (
|
||||
<p className='text-muted-foreground text-sm'>
|
||||
This workflow is triggered by a webhook.
|
||||
</p>
|
||||
)}
|
||||
</Tooltip.Content>
|
||||
</Tooltip.Root>
|
||||
)}
|
||||
|
||||
{isWebhookConfigured && isWebhookDisabled && webhookId && (
|
||||
<Tooltip.Root>
|
||||
<Tooltip.Trigger asChild>
|
||||
<Badge
|
||||
variant='outline'
|
||||
className='cursor-pointer'
|
||||
style={{ borderColor: 'var(--warning)', color: 'var(--warning)' }}
|
||||
className={isWebhookDisabled && webhookId ? 'cursor-pointer' : ''}
|
||||
style={{
|
||||
borderColor: isWebhookDisabled ? 'var(--warning)' : 'var(--success)',
|
||||
color: isWebhookDisabled ? 'var(--warning)' : 'var(--success)',
|
||||
}}
|
||||
onClick={(e) => {
|
||||
e.stopPropagation()
|
||||
reactivateWebhook(webhookId)
|
||||
if (isWebhookDisabled && webhookId) {
|
||||
reactivateWebhook(webhookId)
|
||||
}
|
||||
}}
|
||||
>
|
||||
disabled
|
||||
{isWebhookDisabled ? 'disabled' : 'active'}
|
||||
</Badge>
|
||||
</Tooltip.Trigger>
|
||||
<Tooltip.Content>
|
||||
<span className='text-sm'>Click to reactivate</span>
|
||||
{isWebhookDisabled
|
||||
? 'Click to reactivate'
|
||||
: webhookProvider
|
||||
? `${getProviderName(webhookProvider)} Webhook`
|
||||
: 'Trigger is active'}
|
||||
</Tooltip.Content>
|
||||
</Tooltip.Root>
|
||||
)}
|
||||
|
||||
@@ -215,6 +215,7 @@ export interface SubBlockConfig {
|
||||
connectionDroppable?: boolean
|
||||
hidden?: boolean
|
||||
hideFromPreview?: boolean // Hide this subblock from the workflow block preview
|
||||
noWrapper?: boolean // Render the input directly without wrapper div
|
||||
requiresFeature?: string // Environment variable name that must be truthy for this subblock to be visible
|
||||
description?: string
|
||||
value?: (params: Record<string, any>) => string
|
||||
|
||||
236
apps/sim/hooks/use-auto-save.ts
Normal file
236
apps/sim/hooks/use-auto-save.ts
Normal file
@@ -0,0 +1,236 @@
|
||||
import { useCallback, useEffect, useRef, useState } from 'react'
|
||||
import { createLogger } from '@/lib/logs/console/logger'
|
||||
|
||||
const logger = createLogger('useAutoSave')
|
||||
|
||||
/** Auto-save debounce delay in milliseconds */
|
||||
const AUTO_SAVE_DEBOUNCE_MS = 1500
|
||||
|
||||
/** Delay before enabling auto-save after initial load */
|
||||
const INITIAL_LOAD_DELAY_MS = 500
|
||||
|
||||
/** Default maximum retry attempts */
|
||||
const DEFAULT_MAX_RETRIES = 3
|
||||
|
||||
/** Delay before resetting save status to idle after successful save */
|
||||
const SAVED_STATUS_DISPLAY_MS = 2000
|
||||
|
||||
export type SaveStatus = 'idle' | 'saving' | 'saved' | 'error'
|
||||
|
||||
export interface SaveConfigResult {
|
||||
success: boolean
|
||||
}
|
||||
|
||||
interface UseAutoSaveOptions<T extends SaveConfigResult = SaveConfigResult> {
|
||||
/** Whether auto-save is disabled (e.g., in preview mode) */
|
||||
disabled?: boolean
|
||||
/** Whether a save operation is already in progress externally */
|
||||
isExternallySaving?: boolean
|
||||
/** Maximum retry attempts (default: 3) */
|
||||
maxRetries?: number
|
||||
/** Validate config before saving, return true if valid */
|
||||
validate: () => boolean
|
||||
/** Perform the save operation */
|
||||
onSave: () => Promise<T>
|
||||
/** Optional callback after successful save */
|
||||
onSaveSuccess?: (result: T) => void
|
||||
/** Optional callback after failed save */
|
||||
onSaveError?: (error: Error) => void
|
||||
/** Logger name for debugging */
|
||||
loggerName?: string
|
||||
}
|
||||
|
||||
interface UseAutoSaveReturn {
|
||||
/** Current save status */
|
||||
saveStatus: SaveStatus
|
||||
/** Error message if save failed */
|
||||
errorMessage: string | null
|
||||
/** Current retry count */
|
||||
retryCount: number
|
||||
/** Maximum retries allowed */
|
||||
maxRetries: number
|
||||
/** Whether max retries has been reached */
|
||||
maxRetriesReached: boolean
|
||||
/** Trigger an immediate save attempt (for retry button) */
|
||||
triggerSave: () => Promise<void>
|
||||
/** Call this when config changes to trigger debounced save */
|
||||
onConfigChange: (configFingerprint: string) => void
|
||||
/** Call this when initial load completes to enable auto-save */
|
||||
markInitialLoadComplete: (currentFingerprint: string) => void
|
||||
}
|
||||
|
||||
/**
|
||||
* Shared hook for auto-saving configuration with debouncing, retry limits, and status management.
|
||||
*
|
||||
* @example
|
||||
* ```tsx
|
||||
* const { saveStatus, errorMessage, triggerSave, onConfigChange, markInitialLoadComplete } = useAutoSave({
|
||||
* disabled: isPreview,
|
||||
* isExternallySaving: isSaving,
|
||||
* validate: () => validateRequiredFields(),
|
||||
* onSave: async () => saveConfig(),
|
||||
* onSaveSuccess: (result) => { ... },
|
||||
* })
|
||||
*
|
||||
* // When config fingerprint changes
|
||||
* useEffect(() => {
|
||||
* onConfigChange(configFingerprint)
|
||||
* }, [configFingerprint, onConfigChange])
|
||||
*
|
||||
* // When initial data loads
|
||||
* useEffect(() => {
|
||||
* if (!isLoading && dataId) {
|
||||
* markInitialLoadComplete(configFingerprint)
|
||||
* }
|
||||
* }, [isLoading, dataId, configFingerprint, markInitialLoadComplete])
|
||||
* ```
|
||||
*/
|
||||
export function useAutoSave<T extends SaveConfigResult = SaveConfigResult>({
|
||||
disabled = false,
|
||||
isExternallySaving = false,
|
||||
maxRetries = DEFAULT_MAX_RETRIES,
|
||||
validate,
|
||||
onSave,
|
||||
onSaveSuccess,
|
||||
onSaveError,
|
||||
loggerName,
|
||||
}: UseAutoSaveOptions<T>): UseAutoSaveReturn {
|
||||
const [saveStatus, setSaveStatus] = useState<SaveStatus>('idle')
|
||||
const [errorMessage, setErrorMessage] = useState<string | null>(null)
|
||||
const [retryCount, setRetryCount] = useState(0)
|
||||
|
||||
const autoSaveTimeoutRef = useRef<NodeJS.Timeout | null>(null)
|
||||
const lastSavedConfigRef = useRef<string | null>(null)
|
||||
const isInitialLoadRef = useRef(true)
|
||||
const currentFingerprintRef = useRef<string | null>(null)
|
||||
|
||||
// Clear any pending timeout on unmount
|
||||
useEffect(() => {
|
||||
return () => {
|
||||
if (autoSaveTimeoutRef.current) {
|
||||
clearTimeout(autoSaveTimeoutRef.current)
|
||||
}
|
||||
}
|
||||
}, [])
|
||||
|
||||
const performSave = useCallback(async () => {
|
||||
if (disabled || isExternallySaving) return
|
||||
|
||||
// Final validation check before saving
|
||||
if (!validate()) {
|
||||
setSaveStatus('idle')
|
||||
return
|
||||
}
|
||||
|
||||
setSaveStatus('saving')
|
||||
setErrorMessage(null)
|
||||
|
||||
try {
|
||||
const result = await onSave()
|
||||
|
||||
if (!result.success) {
|
||||
throw new Error('Save operation returned unsuccessful result')
|
||||
}
|
||||
|
||||
// Update last saved config to current
|
||||
lastSavedConfigRef.current = currentFingerprintRef.current
|
||||
setSaveStatus('saved')
|
||||
setErrorMessage(null)
|
||||
setRetryCount(0) // Reset retry count on success
|
||||
|
||||
if (onSaveSuccess) {
|
||||
onSaveSuccess(result)
|
||||
}
|
||||
|
||||
// Reset to idle after display duration
|
||||
setTimeout(() => {
|
||||
setSaveStatus('idle')
|
||||
}, SAVED_STATUS_DISPLAY_MS)
|
||||
|
||||
if (loggerName) {
|
||||
logger.info(`${loggerName}: Auto-save completed successfully`)
|
||||
}
|
||||
} catch (error: unknown) {
|
||||
setSaveStatus('error')
|
||||
const message = error instanceof Error ? error.message : 'An error occurred while saving.'
|
||||
setErrorMessage(message)
|
||||
setRetryCount((prev) => prev + 1)
|
||||
|
||||
if (onSaveError && error instanceof Error) {
|
||||
onSaveError(error)
|
||||
}
|
||||
|
||||
if (loggerName) {
|
||||
logger.error(`${loggerName}: Auto-save failed`, { error })
|
||||
}
|
||||
}
|
||||
}, [disabled, isExternallySaving, validate, onSave, onSaveSuccess, onSaveError, loggerName])
|
||||
|
||||
const onConfigChange = useCallback(
|
||||
(configFingerprint: string) => {
|
||||
currentFingerprintRef.current = configFingerprint
|
||||
|
||||
if (disabled) return
|
||||
|
||||
// Clear any existing timeout
|
||||
if (autoSaveTimeoutRef.current) {
|
||||
clearTimeout(autoSaveTimeoutRef.current)
|
||||
}
|
||||
|
||||
// Skip if initial load hasn't completed
|
||||
if (isInitialLoadRef.current) return
|
||||
|
||||
// Skip if already saving
|
||||
if (saveStatus === 'saving' || isExternallySaving) return
|
||||
|
||||
// Clear error if validation now passes
|
||||
if (saveStatus === 'error' && validate()) {
|
||||
setErrorMessage(null)
|
||||
setSaveStatus('idle')
|
||||
setRetryCount(0) // Reset retry count when config changes
|
||||
}
|
||||
|
||||
// Skip if config hasn't changed
|
||||
if (configFingerprint === lastSavedConfigRef.current) return
|
||||
|
||||
// Skip if validation fails
|
||||
if (!validate()) return
|
||||
|
||||
// Schedule debounced save
|
||||
autoSaveTimeoutRef.current = setTimeout(() => {
|
||||
if (loggerName) {
|
||||
logger.debug(`${loggerName}: Triggering debounced auto-save`)
|
||||
}
|
||||
performSave()
|
||||
}, AUTO_SAVE_DEBOUNCE_MS)
|
||||
},
|
||||
[disabled, saveStatus, isExternallySaving, validate, performSave, loggerName]
|
||||
)
|
||||
|
||||
const markInitialLoadComplete = useCallback((currentFingerprint: string) => {
|
||||
// Delay before enabling auto-save to prevent immediate trigger
|
||||
const timer = setTimeout(() => {
|
||||
isInitialLoadRef.current = false
|
||||
lastSavedConfigRef.current = currentFingerprint
|
||||
currentFingerprintRef.current = currentFingerprint
|
||||
}, INITIAL_LOAD_DELAY_MS)
|
||||
|
||||
return () => clearTimeout(timer)
|
||||
}, [])
|
||||
|
||||
const triggerSave = useCallback(async () => {
|
||||
// Allow retry even if max retries reached (manual trigger)
|
||||
await performSave()
|
||||
}, [performSave])
|
||||
|
||||
return {
|
||||
saveStatus,
|
||||
errorMessage,
|
||||
retryCount,
|
||||
maxRetries,
|
||||
maxRetriesReached: retryCount >= maxRetries,
|
||||
triggerSave,
|
||||
onConfigChange,
|
||||
markInitialLoadComplete,
|
||||
}
|
||||
}
|
||||
@@ -23,15 +23,10 @@ interface ScheduleManagementState {
|
||||
isLoading: boolean
|
||||
isSaving: boolean
|
||||
saveConfig: () => Promise<SaveConfigResult>
|
||||
deleteConfig: () => Promise<boolean>
|
||||
}
|
||||
|
||||
/**
|
||||
* Hook to manage schedule lifecycle for schedule blocks
|
||||
* Handles:
|
||||
* - Loading existing schedules from the API
|
||||
* - Saving schedule configurations
|
||||
* - Deleting schedule configurations
|
||||
*/
|
||||
export function useScheduleManagement({
|
||||
blockId,
|
||||
@@ -45,8 +40,6 @@ export function useScheduleManagement({
|
||||
)
|
||||
|
||||
const isLoading = useSubBlockStore((state) => state.loadingSchedules.has(blockId))
|
||||
const isChecked = useSubBlockStore((state) => state.checkedSchedules.has(blockId))
|
||||
|
||||
const [isSaving, setIsSaving] = useState(false)
|
||||
|
||||
useEffect(() => {
|
||||
@@ -183,49 +176,10 @@ export function useScheduleManagement({
|
||||
}
|
||||
}
|
||||
|
||||
const deleteConfig = async (): Promise<boolean> => {
|
||||
if (isPreview || !scheduleId) {
|
||||
return false
|
||||
}
|
||||
|
||||
try {
|
||||
setIsSaving(true)
|
||||
|
||||
const response = await fetch(`/api/schedules/${scheduleId}`, {
|
||||
method: 'DELETE',
|
||||
headers: { 'Content-Type': 'application/json' },
|
||||
body: JSON.stringify({
|
||||
workspaceId: params.workspaceId as string,
|
||||
}),
|
||||
})
|
||||
|
||||
if (!response.ok) {
|
||||
logger.error('Failed to delete schedule')
|
||||
return false
|
||||
}
|
||||
|
||||
useSubBlockStore.getState().setValue(blockId, 'scheduleId', null)
|
||||
useSubBlockStore.setState((state) => {
|
||||
const newSet = new Set(state.checkedSchedules)
|
||||
newSet.delete(blockId)
|
||||
return { checkedSchedules: newSet }
|
||||
})
|
||||
|
||||
logger.info('Schedule deleted successfully')
|
||||
return true
|
||||
} catch (error) {
|
||||
logger.error('Error deleting schedule:', error)
|
||||
return false
|
||||
} finally {
|
||||
setIsSaving(false)
|
||||
}
|
||||
}
|
||||
|
||||
return {
|
||||
scheduleId,
|
||||
isLoading,
|
||||
isSaving,
|
||||
saveConfig,
|
||||
deleteConfig,
|
||||
}
|
||||
}
|
||||
|
||||
@@ -3,7 +3,7 @@ import { useSubBlockStore } from '@/stores/workflows/subblock/store'
|
||||
import { getTrigger, isTriggerValid } from '@/triggers'
|
||||
import { SYSTEM_SUBBLOCK_IDS } from '@/triggers/constants'
|
||||
|
||||
const logger = createLogger('useTriggerConfigAggregation')
|
||||
const logger = createLogger('getTriggerConfigAggregation')
|
||||
|
||||
/**
|
||||
* Maps old trigger config field names to new subblock IDs for backward compatibility.
|
||||
@@ -34,7 +34,7 @@ function mapOldFieldNameToNewSubBlockId(oldFieldName: string): string {
|
||||
* @returns The aggregated config object, or null if no valid config
|
||||
*/
|
||||
|
||||
export function useTriggerConfigAggregation(
|
||||
export function getTriggerConfigAggregation(
|
||||
blockId: string,
|
||||
triggerId: string | undefined
|
||||
): Record<string, any> | null {
|
||||
|
||||
@@ -16,14 +16,18 @@ interface UseWebhookManagementProps {
|
||||
isPreview?: boolean
|
||||
}
|
||||
|
||||
interface SaveConfigResult {
|
||||
success: boolean
|
||||
webhookId?: string
|
||||
}
|
||||
|
||||
interface WebhookManagementState {
|
||||
webhookUrl: string
|
||||
webhookPath: string
|
||||
webhookId: string | null
|
||||
isLoading: boolean
|
||||
isSaving: boolean
|
||||
saveConfig: () => Promise<boolean>
|
||||
deleteConfig: () => Promise<boolean>
|
||||
saveConfig: () => Promise<SaveConfigResult>
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -81,10 +85,6 @@ function resolveEffectiveTriggerId(
|
||||
|
||||
/**
|
||||
* Hook to manage webhook lifecycle for trigger blocks
|
||||
* Handles:
|
||||
* - Pre-generating webhook URLs based on blockId (without creating webhook)
|
||||
* - Loading existing webhooks from the API
|
||||
* - Saving and deleting webhook configurations
|
||||
*/
|
||||
export function useWebhookManagement({
|
||||
blockId,
|
||||
@@ -103,7 +103,6 @@ export function useWebhookManagement({
|
||||
useCallback((state) => state.getValue(blockId, 'triggerPath') as string | null, [blockId])
|
||||
)
|
||||
const isLoading = useSubBlockStore((state) => state.loadingWebhooks.has(blockId))
|
||||
const isChecked = useSubBlockStore((state) => state.checkedWebhooks.has(blockId))
|
||||
|
||||
const webhookUrl = useMemo(() => {
|
||||
if (!webhookPath) {
|
||||
@@ -211,9 +210,9 @@ export function useWebhookManagement({
|
||||
const createWebhook = async (
|
||||
effectiveTriggerId: string | undefined,
|
||||
selectedCredentialId: string | null
|
||||
): Promise<boolean> => {
|
||||
): Promise<SaveConfigResult> => {
|
||||
if (!triggerDef || !effectiveTriggerId) {
|
||||
return false
|
||||
return { success: false }
|
||||
}
|
||||
|
||||
const triggerConfig = useSubBlockStore.getState().getValue(blockId, 'triggerConfig')
|
||||
@@ -266,14 +265,14 @@ export function useWebhookManagement({
|
||||
blockId,
|
||||
})
|
||||
|
||||
return true
|
||||
return { success: true, webhookId: savedWebhookId }
|
||||
}
|
||||
|
||||
const updateWebhook = async (
|
||||
webhookIdToUpdate: string,
|
||||
effectiveTriggerId: string | undefined,
|
||||
selectedCredentialId: string | null
|
||||
): Promise<boolean> => {
|
||||
): Promise<SaveConfigResult> => {
|
||||
const triggerConfig = useSubBlockStore.getState().getValue(blockId, 'triggerConfig')
|
||||
|
||||
const response = await fetch(`/api/webhooks/${webhookIdToUpdate}`, {
|
||||
@@ -310,12 +309,12 @@ export function useWebhookManagement({
|
||||
}
|
||||
|
||||
logger.info('Trigger config saved successfully', { blockId, webhookId: webhookIdToUpdate })
|
||||
return true
|
||||
return { success: true, webhookId: webhookIdToUpdate }
|
||||
}
|
||||
|
||||
const saveConfig = async (): Promise<boolean> => {
|
||||
const saveConfig = async (): Promise<SaveConfigResult> => {
|
||||
if (isPreview || !triggerDef) {
|
||||
return false
|
||||
return { success: false }
|
||||
}
|
||||
|
||||
const effectiveTriggerId = resolveEffectiveTriggerId(blockId, triggerId)
|
||||
@@ -339,41 +338,6 @@ export function useWebhookManagement({
|
||||
}
|
||||
}
|
||||
|
||||
const deleteConfig = async (): Promise<boolean> => {
|
||||
if (isPreview || !webhookId) {
|
||||
return false
|
||||
}
|
||||
|
||||
try {
|
||||
setIsSaving(true)
|
||||
|
||||
const response = await fetch(`/api/webhooks/${webhookId}`, {
|
||||
method: 'DELETE',
|
||||
})
|
||||
|
||||
if (!response.ok) {
|
||||
logger.error('Failed to delete webhook')
|
||||
return false
|
||||
}
|
||||
|
||||
useSubBlockStore.getState().setValue(blockId, 'triggerPath', '')
|
||||
useSubBlockStore.getState().setValue(blockId, 'webhookId', null)
|
||||
useSubBlockStore.setState((state) => {
|
||||
const newSet = new Set(state.checkedWebhooks)
|
||||
newSet.delete(blockId)
|
||||
return { checkedWebhooks: newSet }
|
||||
})
|
||||
|
||||
logger.info('Webhook deleted successfully')
|
||||
return true
|
||||
} catch (error) {
|
||||
logger.error('Error deleting webhook:', error)
|
||||
return false
|
||||
} finally {
|
||||
setIsSaving(false)
|
||||
}
|
||||
}
|
||||
|
||||
return {
|
||||
webhookUrl,
|
||||
webhookPath: webhookPath || blockId,
|
||||
@@ -381,6 +345,5 @@ export function useWebhookManagement({
|
||||
isLoading,
|
||||
isSaving,
|
||||
saveConfig,
|
||||
deleteConfig,
|
||||
}
|
||||
}
|
||||
|
||||
@@ -47,6 +47,14 @@ export const airtableWebhookTrigger: TriggerConfig = {
|
||||
defaultValue: false,
|
||||
mode: 'trigger',
|
||||
},
|
||||
{
|
||||
id: 'triggerSave',
|
||||
title: '',
|
||||
type: 'trigger-save',
|
||||
hideFromPreview: true,
|
||||
mode: 'trigger',
|
||||
triggerId: 'airtable_webhook',
|
||||
},
|
||||
{
|
||||
id: 'triggerInstructions',
|
||||
title: 'Setup Instructions',
|
||||
@@ -67,14 +75,6 @@ export const airtableWebhookTrigger: TriggerConfig = {
|
||||
.join(''),
|
||||
mode: 'trigger',
|
||||
},
|
||||
{
|
||||
id: 'triggerSave',
|
||||
title: '',
|
||||
type: 'trigger-save',
|
||||
hideFromPreview: true,
|
||||
mode: 'trigger',
|
||||
triggerId: 'airtable_webhook',
|
||||
},
|
||||
],
|
||||
|
||||
outputs: {
|
||||
|
||||
@@ -38,6 +38,18 @@ export const calendlyInviteeCanceledTrigger: TriggerConfig = {
|
||||
value: 'calendly_invitee_canceled',
|
||||
},
|
||||
},
|
||||
{
|
||||
id: 'triggerSave',
|
||||
title: '',
|
||||
type: 'trigger-save',
|
||||
hideFromPreview: true,
|
||||
mode: 'trigger',
|
||||
triggerId: 'calendly_invitee_canceled',
|
||||
condition: {
|
||||
field: 'selectedTriggerId',
|
||||
value: 'calendly_invitee_canceled',
|
||||
},
|
||||
},
|
||||
{
|
||||
id: 'triggerInstructions',
|
||||
title: 'Setup Instructions',
|
||||
@@ -47,7 +59,7 @@ export const calendlyInviteeCanceledTrigger: TriggerConfig = {
|
||||
'<strong>Note:</strong> This trigger requires a paid Calendly subscription (Professional, Teams, or Enterprise plan).',
|
||||
'Get your Personal Access Token from <strong>Settings > Integrations > API & Webhooks</strong> in your Calendly account.',
|
||||
'Use the "Get Current User" operation in a Calendly block to retrieve your Organization URI.',
|
||||
'The webhook will be automatically created in Calendly when you save this trigger.',
|
||||
'The webhook will be automatically created in Calendly once you complete the configuration above.',
|
||||
'This webhook triggers when an invitee cancels an event. The payload includes cancellation details and reason.',
|
||||
]
|
||||
.map(
|
||||
@@ -61,18 +73,6 @@ export const calendlyInviteeCanceledTrigger: TriggerConfig = {
|
||||
value: 'calendly_invitee_canceled',
|
||||
},
|
||||
},
|
||||
{
|
||||
id: 'triggerSave',
|
||||
title: '',
|
||||
type: 'trigger-save',
|
||||
hideFromPreview: true,
|
||||
mode: 'trigger',
|
||||
triggerId: 'calendly_invitee_canceled',
|
||||
condition: {
|
||||
field: 'selectedTriggerId',
|
||||
value: 'calendly_invitee_canceled',
|
||||
},
|
||||
},
|
||||
],
|
||||
|
||||
outputs: buildInviteeOutputs(),
|
||||
|
||||
@@ -47,6 +47,18 @@ export const calendlyInviteeCreatedTrigger: TriggerConfig = {
|
||||
value: 'calendly_invitee_created',
|
||||
},
|
||||
},
|
||||
{
|
||||
id: 'triggerSave',
|
||||
title: '',
|
||||
type: 'trigger-save',
|
||||
hideFromPreview: true,
|
||||
mode: 'trigger',
|
||||
triggerId: 'calendly_invitee_created',
|
||||
condition: {
|
||||
field: 'selectedTriggerId',
|
||||
value: 'calendly_invitee_created',
|
||||
},
|
||||
},
|
||||
{
|
||||
id: 'triggerInstructions',
|
||||
title: 'Setup Instructions',
|
||||
@@ -56,7 +68,7 @@ export const calendlyInviteeCreatedTrigger: TriggerConfig = {
|
||||
'<strong>Note:</strong> This trigger requires a paid Calendly subscription (Professional, Teams, or Enterprise plan).',
|
||||
'Get your Personal Access Token from <strong>Settings > Integrations > API & Webhooks</strong> in your Calendly account.',
|
||||
'Use the "Get Current User" operation in a Calendly block to retrieve your Organization URI.',
|
||||
'The webhook will be automatically created in Calendly when you save this trigger.',
|
||||
'The webhook will be automatically created in Calendly once you complete the configuration above.',
|
||||
'This webhook triggers when an invitee schedules a new event. Rescheduling triggers both cancellation and creation events.',
|
||||
]
|
||||
.map(
|
||||
@@ -70,18 +82,6 @@ export const calendlyInviteeCreatedTrigger: TriggerConfig = {
|
||||
value: 'calendly_invitee_created',
|
||||
},
|
||||
},
|
||||
{
|
||||
id: 'triggerSave',
|
||||
title: '',
|
||||
type: 'trigger-save',
|
||||
hideFromPreview: true,
|
||||
mode: 'trigger',
|
||||
triggerId: 'calendly_invitee_created',
|
||||
condition: {
|
||||
field: 'selectedTriggerId',
|
||||
value: 'calendly_invitee_created',
|
||||
},
|
||||
},
|
||||
],
|
||||
|
||||
outputs: buildInviteeOutputs(),
|
||||
|
||||
@@ -38,6 +38,18 @@ export const calendlyRoutingFormSubmittedTrigger: TriggerConfig = {
|
||||
value: 'calendly_routing_form_submitted',
|
||||
},
|
||||
},
|
||||
{
|
||||
id: 'triggerSave',
|
||||
title: '',
|
||||
type: 'trigger-save',
|
||||
hideFromPreview: true,
|
||||
mode: 'trigger',
|
||||
triggerId: 'calendly_routing_form_submitted',
|
||||
condition: {
|
||||
field: 'selectedTriggerId',
|
||||
value: 'calendly_routing_form_submitted',
|
||||
},
|
||||
},
|
||||
{
|
||||
id: 'triggerInstructions',
|
||||
title: 'Setup Instructions',
|
||||
@@ -47,7 +59,7 @@ export const calendlyRoutingFormSubmittedTrigger: TriggerConfig = {
|
||||
'<strong>Note:</strong> This trigger requires a paid Calendly subscription (Professional, Teams, or Enterprise plan).',
|
||||
'Get your Personal Access Token from <strong>Settings > Integrations > API & Webhooks</strong> in your Calendly account.',
|
||||
'Use the "Get Current User" operation in a Calendly block to retrieve your Organization URI.',
|
||||
'The webhook will be automatically created in Calendly when you save this trigger.',
|
||||
'The webhook will be automatically created in Calendly once you complete the configuration above.',
|
||||
'This webhook triggers when someone submits a routing form, regardless of whether they book an event.',
|
||||
]
|
||||
.map(
|
||||
@@ -61,18 +73,6 @@ export const calendlyRoutingFormSubmittedTrigger: TriggerConfig = {
|
||||
value: 'calendly_routing_form_submitted',
|
||||
},
|
||||
},
|
||||
{
|
||||
id: 'triggerSave',
|
||||
title: '',
|
||||
type: 'trigger-save',
|
||||
hideFromPreview: true,
|
||||
mode: 'trigger',
|
||||
triggerId: 'calendly_routing_form_submitted',
|
||||
condition: {
|
||||
field: 'selectedTriggerId',
|
||||
value: 'calendly_routing_form_submitted',
|
||||
},
|
||||
},
|
||||
],
|
||||
|
||||
outputs: buildRoutingFormOutputs(),
|
||||
|
||||
@@ -37,6 +37,18 @@ export const calendlyWebhookTrigger: TriggerConfig = {
|
||||
value: 'calendly_webhook',
|
||||
},
|
||||
},
|
||||
{
|
||||
id: 'triggerSave',
|
||||
title: '',
|
||||
type: 'trigger-save',
|
||||
hideFromPreview: true,
|
||||
mode: 'trigger',
|
||||
triggerId: 'calendly_webhook',
|
||||
condition: {
|
||||
field: 'selectedTriggerId',
|
||||
value: 'calendly_webhook',
|
||||
},
|
||||
},
|
||||
{
|
||||
id: 'triggerInstructions',
|
||||
title: 'Setup Instructions',
|
||||
@@ -46,7 +58,7 @@ export const calendlyWebhookTrigger: TriggerConfig = {
|
||||
'<strong>Note:</strong> This trigger requires a paid Calendly subscription (Professional, Teams, or Enterprise plan).',
|
||||
'Get your Personal Access Token from <strong>Settings > Integrations > API & Webhooks</strong> in your Calendly account.',
|
||||
'Use the "Get Current User" operation in a Calendly block to retrieve your Organization URI.',
|
||||
'The webhook will be automatically created in Calendly when you save this trigger.',
|
||||
'The webhook will be automatically created in Calendly once you complete the configuration above.',
|
||||
'This webhook subscribes to all Calendly events (invitee created, invitee canceled, and routing form submitted). Use the <code>event</code> field in the payload to determine the event type.',
|
||||
]
|
||||
.map(
|
||||
@@ -60,18 +72,6 @@ export const calendlyWebhookTrigger: TriggerConfig = {
|
||||
value: 'calendly_webhook',
|
||||
},
|
||||
},
|
||||
{
|
||||
id: 'triggerSave',
|
||||
title: '',
|
||||
type: 'trigger-save',
|
||||
hideFromPreview: true,
|
||||
mode: 'trigger',
|
||||
triggerId: 'calendly_webhook',
|
||||
condition: {
|
||||
field: 'selectedTriggerId',
|
||||
value: 'calendly_webhook',
|
||||
},
|
||||
},
|
||||
],
|
||||
|
||||
outputs: {
|
||||
|
||||
@@ -56,6 +56,14 @@ export const genericWebhookTrigger: TriggerConfig = {
|
||||
'Define the expected JSON input schema for this webhook (optional). Use type "files" for file uploads.',
|
||||
mode: 'trigger',
|
||||
},
|
||||
{
|
||||
id: 'triggerSave',
|
||||
title: '',
|
||||
type: 'trigger-save',
|
||||
hideFromPreview: true,
|
||||
mode: 'trigger',
|
||||
triggerId: 'generic_webhook',
|
||||
},
|
||||
{
|
||||
id: 'triggerInstructions',
|
||||
title: 'Setup Instructions',
|
||||
@@ -76,14 +84,6 @@ export const genericWebhookTrigger: TriggerConfig = {
|
||||
.join(''),
|
||||
mode: 'trigger',
|
||||
},
|
||||
{
|
||||
id: 'triggerSave',
|
||||
title: '',
|
||||
type: 'trigger-save',
|
||||
hideFromPreview: true,
|
||||
mode: 'trigger',
|
||||
triggerId: 'generic_webhook',
|
||||
},
|
||||
],
|
||||
|
||||
outputs: {},
|
||||
|
||||
@@ -75,6 +75,18 @@ export const githubIssueClosedTrigger: TriggerConfig = {
|
||||
value: 'github_issue_closed',
|
||||
},
|
||||
},
|
||||
{
|
||||
id: 'triggerSave',
|
||||
title: '',
|
||||
type: 'trigger-save',
|
||||
hideFromPreview: true,
|
||||
mode: 'trigger',
|
||||
triggerId: 'github_issue_closed',
|
||||
condition: {
|
||||
field: 'selectedTriggerId',
|
||||
value: 'github_issue_closed',
|
||||
},
|
||||
},
|
||||
{
|
||||
id: 'triggerInstructions',
|
||||
title: 'Setup Instructions',
|
||||
@@ -101,18 +113,6 @@ export const githubIssueClosedTrigger: TriggerConfig = {
|
||||
value: 'github_issue_closed',
|
||||
},
|
||||
},
|
||||
{
|
||||
id: 'triggerSave',
|
||||
title: '',
|
||||
type: 'trigger-save',
|
||||
hideFromPreview: true,
|
||||
mode: 'trigger',
|
||||
triggerId: 'github_issue_closed',
|
||||
condition: {
|
||||
field: 'selectedTriggerId',
|
||||
value: 'github_issue_closed',
|
||||
},
|
||||
},
|
||||
],
|
||||
|
||||
outputs: {
|
||||
|
||||
@@ -75,6 +75,18 @@ export const githubIssueCommentTrigger: TriggerConfig = {
|
||||
value: 'github_issue_comment',
|
||||
},
|
||||
},
|
||||
{
|
||||
id: 'triggerSave',
|
||||
title: '',
|
||||
type: 'trigger-save',
|
||||
hideFromPreview: true,
|
||||
mode: 'trigger',
|
||||
triggerId: 'github_issue_comment',
|
||||
condition: {
|
||||
field: 'selectedTriggerId',
|
||||
value: 'github_issue_comment',
|
||||
},
|
||||
},
|
||||
{
|
||||
id: 'triggerInstructions',
|
||||
title: 'Setup Instructions',
|
||||
@@ -102,18 +114,6 @@ export const githubIssueCommentTrigger: TriggerConfig = {
|
||||
value: 'github_issue_comment',
|
||||
},
|
||||
},
|
||||
{
|
||||
id: 'triggerSave',
|
||||
title: '',
|
||||
type: 'trigger-save',
|
||||
hideFromPreview: true,
|
||||
mode: 'trigger',
|
||||
triggerId: 'github_issue_comment',
|
||||
condition: {
|
||||
field: 'selectedTriggerId',
|
||||
value: 'github_issue_comment',
|
||||
},
|
||||
},
|
||||
],
|
||||
|
||||
outputs: {
|
||||
|
||||
@@ -96,6 +96,18 @@ export const githubIssueOpenedTrigger: TriggerConfig = {
|
||||
value: 'github_issue_opened',
|
||||
},
|
||||
},
|
||||
{
|
||||
id: 'triggerSave',
|
||||
title: '',
|
||||
type: 'trigger-save',
|
||||
hideFromPreview: true,
|
||||
mode: 'trigger',
|
||||
triggerId: 'github_issue_opened',
|
||||
condition: {
|
||||
field: 'selectedTriggerId',
|
||||
value: 'github_issue_opened',
|
||||
},
|
||||
},
|
||||
{
|
||||
id: 'triggerInstructions',
|
||||
title: 'Setup Instructions',
|
||||
@@ -122,18 +134,6 @@ export const githubIssueOpenedTrigger: TriggerConfig = {
|
||||
value: 'github_issue_opened',
|
||||
},
|
||||
},
|
||||
{
|
||||
id: 'triggerSave',
|
||||
title: '',
|
||||
type: 'trigger-save',
|
||||
hideFromPreview: true,
|
||||
mode: 'trigger',
|
||||
triggerId: 'github_issue_opened',
|
||||
condition: {
|
||||
field: 'selectedTriggerId',
|
||||
value: 'github_issue_opened',
|
||||
},
|
||||
},
|
||||
],
|
||||
|
||||
outputs: {
|
||||
|
||||
@@ -76,6 +76,18 @@ export const githubPRClosedTrigger: TriggerConfig = {
|
||||
value: 'github_pr_closed',
|
||||
},
|
||||
},
|
||||
{
|
||||
id: 'triggerSave',
|
||||
title: '',
|
||||
type: 'trigger-save',
|
||||
hideFromPreview: true,
|
||||
mode: 'trigger',
|
||||
triggerId: 'github_pr_closed',
|
||||
condition: {
|
||||
field: 'selectedTriggerId',
|
||||
value: 'github_pr_closed',
|
||||
},
|
||||
},
|
||||
{
|
||||
id: 'triggerInstructions',
|
||||
title: 'Setup Instructions',
|
||||
@@ -102,18 +114,6 @@ export const githubPRClosedTrigger: TriggerConfig = {
|
||||
value: 'github_pr_closed',
|
||||
},
|
||||
},
|
||||
{
|
||||
id: 'triggerSave',
|
||||
title: '',
|
||||
type: 'trigger-save',
|
||||
hideFromPreview: true,
|
||||
mode: 'trigger',
|
||||
triggerId: 'github_pr_closed',
|
||||
condition: {
|
||||
field: 'selectedTriggerId',
|
||||
value: 'github_pr_closed',
|
||||
},
|
||||
},
|
||||
],
|
||||
|
||||
outputs: {
|
||||
|
||||
@@ -75,6 +75,18 @@ export const githubPRCommentTrigger: TriggerConfig = {
|
||||
value: 'github_pr_comment',
|
||||
},
|
||||
},
|
||||
{
|
||||
id: 'triggerSave',
|
||||
title: '',
|
||||
type: 'trigger-save',
|
||||
hideFromPreview: true,
|
||||
mode: 'trigger',
|
||||
triggerId: 'github_pr_comment',
|
||||
condition: {
|
||||
field: 'selectedTriggerId',
|
||||
value: 'github_pr_comment',
|
||||
},
|
||||
},
|
||||
{
|
||||
id: 'triggerInstructions',
|
||||
title: 'Setup Instructions',
|
||||
@@ -102,18 +114,6 @@ export const githubPRCommentTrigger: TriggerConfig = {
|
||||
value: 'github_pr_comment',
|
||||
},
|
||||
},
|
||||
{
|
||||
id: 'triggerSave',
|
||||
title: '',
|
||||
type: 'trigger-save',
|
||||
hideFromPreview: true,
|
||||
mode: 'trigger',
|
||||
triggerId: 'github_pr_comment',
|
||||
condition: {
|
||||
field: 'selectedTriggerId',
|
||||
value: 'github_pr_comment',
|
||||
},
|
||||
},
|
||||
],
|
||||
|
||||
outputs: {
|
||||
|
||||
@@ -75,6 +75,18 @@ export const githubPRMergedTrigger: TriggerConfig = {
|
||||
value: 'github_pr_merged',
|
||||
},
|
||||
},
|
||||
{
|
||||
id: 'triggerSave',
|
||||
title: '',
|
||||
type: 'trigger-save',
|
||||
hideFromPreview: true,
|
||||
mode: 'trigger',
|
||||
triggerId: 'github_pr_merged',
|
||||
condition: {
|
||||
field: 'selectedTriggerId',
|
||||
value: 'github_pr_merged',
|
||||
},
|
||||
},
|
||||
{
|
||||
id: 'triggerInstructions',
|
||||
title: 'Setup Instructions',
|
||||
@@ -101,18 +113,6 @@ export const githubPRMergedTrigger: TriggerConfig = {
|
||||
value: 'github_pr_merged',
|
||||
},
|
||||
},
|
||||
{
|
||||
id: 'triggerSave',
|
||||
title: '',
|
||||
type: 'trigger-save',
|
||||
hideFromPreview: true,
|
||||
mode: 'trigger',
|
||||
triggerId: 'github_pr_merged',
|
||||
condition: {
|
||||
field: 'selectedTriggerId',
|
||||
value: 'github_pr_merged',
|
||||
},
|
||||
},
|
||||
],
|
||||
|
||||
outputs: {
|
||||
|
||||
@@ -75,6 +75,18 @@ export const githubPROpenedTrigger: TriggerConfig = {
|
||||
value: 'github_pr_opened',
|
||||
},
|
||||
},
|
||||
{
|
||||
id: 'triggerSave',
|
||||
title: '',
|
||||
type: 'trigger-save',
|
||||
hideFromPreview: true,
|
||||
mode: 'trigger',
|
||||
triggerId: 'github_pr_opened',
|
||||
condition: {
|
||||
field: 'selectedTriggerId',
|
||||
value: 'github_pr_opened',
|
||||
},
|
||||
},
|
||||
{
|
||||
id: 'triggerInstructions',
|
||||
title: 'Setup Instructions',
|
||||
@@ -101,18 +113,6 @@ export const githubPROpenedTrigger: TriggerConfig = {
|
||||
value: 'github_pr_opened',
|
||||
},
|
||||
},
|
||||
{
|
||||
id: 'triggerSave',
|
||||
title: '',
|
||||
type: 'trigger-save',
|
||||
hideFromPreview: true,
|
||||
mode: 'trigger',
|
||||
triggerId: 'github_pr_opened',
|
||||
condition: {
|
||||
field: 'selectedTriggerId',
|
||||
value: 'github_pr_opened',
|
||||
},
|
||||
},
|
||||
],
|
||||
|
||||
outputs: {
|
||||
|
||||
@@ -76,6 +76,18 @@ export const githubPRReviewedTrigger: TriggerConfig = {
|
||||
value: 'github_pr_reviewed',
|
||||
},
|
||||
},
|
||||
{
|
||||
id: 'triggerSave',
|
||||
title: '',
|
||||
type: 'trigger-save',
|
||||
hideFromPreview: true,
|
||||
mode: 'trigger',
|
||||
triggerId: 'github_pr_reviewed',
|
||||
condition: {
|
||||
field: 'selectedTriggerId',
|
||||
value: 'github_pr_reviewed',
|
||||
},
|
||||
},
|
||||
{
|
||||
id: 'triggerInstructions',
|
||||
title: 'Setup Instructions',
|
||||
@@ -102,18 +114,6 @@ export const githubPRReviewedTrigger: TriggerConfig = {
|
||||
value: 'github_pr_reviewed',
|
||||
},
|
||||
},
|
||||
{
|
||||
id: 'triggerSave',
|
||||
title: '',
|
||||
type: 'trigger-save',
|
||||
hideFromPreview: true,
|
||||
mode: 'trigger',
|
||||
triggerId: 'github_pr_reviewed',
|
||||
condition: {
|
||||
field: 'selectedTriggerId',
|
||||
value: 'github_pr_reviewed',
|
||||
},
|
||||
},
|
||||
],
|
||||
|
||||
outputs: {
|
||||
|
||||
@@ -75,6 +75,18 @@ export const githubPushTrigger: TriggerConfig = {
|
||||
value: 'github_push',
|
||||
},
|
||||
},
|
||||
{
|
||||
id: 'triggerSave',
|
||||
title: '',
|
||||
type: 'trigger-save',
|
||||
hideFromPreview: true,
|
||||
mode: 'trigger',
|
||||
triggerId: 'github_push',
|
||||
condition: {
|
||||
field: 'selectedTriggerId',
|
||||
value: 'github_push',
|
||||
},
|
||||
},
|
||||
{
|
||||
id: 'triggerInstructions',
|
||||
title: 'Setup Instructions',
|
||||
@@ -101,18 +113,6 @@ export const githubPushTrigger: TriggerConfig = {
|
||||
value: 'github_push',
|
||||
},
|
||||
},
|
||||
{
|
||||
id: 'triggerSave',
|
||||
title: '',
|
||||
type: 'trigger-save',
|
||||
hideFromPreview: true,
|
||||
mode: 'trigger',
|
||||
triggerId: 'github_push',
|
||||
condition: {
|
||||
field: 'selectedTriggerId',
|
||||
value: 'github_push',
|
||||
},
|
||||
},
|
||||
],
|
||||
|
||||
outputs: {
|
||||
|
||||
@@ -75,6 +75,18 @@ export const githubReleasePublishedTrigger: TriggerConfig = {
|
||||
value: 'github_release_published',
|
||||
},
|
||||
},
|
||||
{
|
||||
id: 'triggerSave',
|
||||
title: '',
|
||||
type: 'trigger-save',
|
||||
hideFromPreview: true,
|
||||
mode: 'trigger',
|
||||
triggerId: 'github_release_published',
|
||||
condition: {
|
||||
field: 'selectedTriggerId',
|
||||
value: 'github_release_published',
|
||||
},
|
||||
},
|
||||
{
|
||||
id: 'triggerInstructions',
|
||||
title: 'Setup Instructions',
|
||||
@@ -101,18 +113,6 @@ export const githubReleasePublishedTrigger: TriggerConfig = {
|
||||
value: 'github_release_published',
|
||||
},
|
||||
},
|
||||
{
|
||||
id: 'triggerSave',
|
||||
title: '',
|
||||
type: 'trigger-save',
|
||||
hideFromPreview: true,
|
||||
mode: 'trigger',
|
||||
triggerId: 'github_release_published',
|
||||
condition: {
|
||||
field: 'selectedTriggerId',
|
||||
value: 'github_release_published',
|
||||
},
|
||||
},
|
||||
],
|
||||
|
||||
outputs: {
|
||||
|
||||
@@ -72,6 +72,18 @@ export const githubWebhookTrigger: TriggerConfig = {
|
||||
value: 'github_webhook',
|
||||
},
|
||||
},
|
||||
{
|
||||
id: 'triggerSave',
|
||||
title: '',
|
||||
type: 'trigger-save',
|
||||
hideFromPreview: true,
|
||||
mode: 'trigger',
|
||||
triggerId: 'github_webhook',
|
||||
condition: {
|
||||
field: 'selectedTriggerId',
|
||||
value: 'github_webhook',
|
||||
},
|
||||
},
|
||||
{
|
||||
id: 'triggerInstructions',
|
||||
title: 'Setup Instructions',
|
||||
@@ -98,18 +110,6 @@ export const githubWebhookTrigger: TriggerConfig = {
|
||||
value: 'github_webhook',
|
||||
},
|
||||
},
|
||||
{
|
||||
id: 'triggerSave',
|
||||
title: '',
|
||||
type: 'trigger-save',
|
||||
hideFromPreview: true,
|
||||
mode: 'trigger',
|
||||
triggerId: 'github_webhook',
|
||||
condition: {
|
||||
field: 'selectedTriggerId',
|
||||
value: 'github_webhook',
|
||||
},
|
||||
},
|
||||
],
|
||||
|
||||
outputs: {
|
||||
|
||||
@@ -76,6 +76,18 @@ export const githubWorkflowRunTrigger: TriggerConfig = {
|
||||
value: 'github_workflow_run',
|
||||
},
|
||||
},
|
||||
{
|
||||
id: 'triggerSave',
|
||||
title: '',
|
||||
type: 'trigger-save',
|
||||
hideFromPreview: true,
|
||||
mode: 'trigger',
|
||||
triggerId: 'github_workflow_run',
|
||||
condition: {
|
||||
field: 'selectedTriggerId',
|
||||
value: 'github_workflow_run',
|
||||
},
|
||||
},
|
||||
{
|
||||
id: 'triggerInstructions',
|
||||
title: 'Setup Instructions',
|
||||
@@ -102,18 +114,6 @@ export const githubWorkflowRunTrigger: TriggerConfig = {
|
||||
value: 'github_workflow_run',
|
||||
},
|
||||
},
|
||||
{
|
||||
id: 'triggerSave',
|
||||
title: '',
|
||||
type: 'trigger-save',
|
||||
hideFromPreview: true,
|
||||
mode: 'trigger',
|
||||
triggerId: 'github_workflow_run',
|
||||
condition: {
|
||||
field: 'selectedTriggerId',
|
||||
value: 'github_workflow_run',
|
||||
},
|
||||
},
|
||||
],
|
||||
|
||||
outputs: {
|
||||
|
||||
@@ -104,6 +104,14 @@ export const gmailPollingTrigger: TriggerConfig = {
|
||||
required: false,
|
||||
mode: 'trigger',
|
||||
},
|
||||
{
|
||||
id: 'triggerSave',
|
||||
title: '',
|
||||
type: 'trigger-save',
|
||||
hideFromPreview: true,
|
||||
mode: 'trigger',
|
||||
triggerId: 'gmail_poller',
|
||||
},
|
||||
{
|
||||
id: 'triggerInstructions',
|
||||
title: 'Setup Instructions',
|
||||
@@ -121,14 +129,6 @@ export const gmailPollingTrigger: TriggerConfig = {
|
||||
.join(''),
|
||||
mode: 'trigger',
|
||||
},
|
||||
{
|
||||
id: 'triggerSave',
|
||||
title: '',
|
||||
type: 'trigger-save',
|
||||
hideFromPreview: true,
|
||||
mode: 'trigger',
|
||||
triggerId: 'gmail_poller',
|
||||
},
|
||||
],
|
||||
|
||||
outputs: {
|
||||
|
||||
@@ -59,6 +59,14 @@ export const googleFormsWebhookTrigger: TriggerConfig = {
|
||||
defaultValue: true,
|
||||
mode: 'trigger',
|
||||
},
|
||||
{
|
||||
id: 'triggerSave',
|
||||
title: '',
|
||||
type: 'trigger-save',
|
||||
hideFromPreview: true,
|
||||
mode: 'trigger',
|
||||
triggerId: 'google_forms_webhook',
|
||||
},
|
||||
{
|
||||
id: 'triggerInstructions',
|
||||
title: 'Setup Instructions',
|
||||
@@ -86,12 +94,12 @@ export const googleFormsWebhookTrigger: TriggerConfig = {
|
||||
const script = `function onFormSubmit(e) {
|
||||
const WEBHOOK_URL = "{{WEBHOOK_URL}}";
|
||||
const SHARED_SECRET = "{{SHARED_SECRET}}";
|
||||
|
||||
|
||||
try {
|
||||
const form = FormApp.getActiveForm();
|
||||
const formResponse = e.response;
|
||||
const itemResponses = formResponse.getItemResponses();
|
||||
|
||||
|
||||
// Build answers object
|
||||
const answers = {};
|
||||
for (var i = 0; i < itemResponses.length; i++) {
|
||||
@@ -100,7 +108,7 @@ export const googleFormsWebhookTrigger: TriggerConfig = {
|
||||
const answer = itemResponse.getResponse();
|
||||
answers[question] = answer;
|
||||
}
|
||||
|
||||
|
||||
// Build payload
|
||||
const payload = {
|
||||
provider: "google_forms",
|
||||
@@ -110,7 +118,7 @@ export const googleFormsWebhookTrigger: TriggerConfig = {
|
||||
lastSubmittedTime: formResponse.getTimestamp().toISOString(),
|
||||
answers: answers
|
||||
};
|
||||
|
||||
|
||||
// Send to webhook
|
||||
const options = {
|
||||
method: "post",
|
||||
@@ -121,9 +129,9 @@ export const googleFormsWebhookTrigger: TriggerConfig = {
|
||||
payload: JSON.stringify(payload),
|
||||
muteHttpExceptions: true
|
||||
};
|
||||
|
||||
|
||||
const response = UrlFetchApp.fetch(WEBHOOK_URL, options);
|
||||
|
||||
|
||||
if (response.getResponseCode() !== 200) {
|
||||
Logger.log("Webhook failed: " + response.getContentText());
|
||||
} else {
|
||||
@@ -145,14 +153,6 @@ export const googleFormsWebhookTrigger: TriggerConfig = {
|
||||
description: 'Copy this code and paste it into your Google Forms Apps Script editor',
|
||||
mode: 'trigger',
|
||||
},
|
||||
{
|
||||
id: 'triggerSave',
|
||||
title: '',
|
||||
type: 'trigger-save',
|
||||
hideFromPreview: true,
|
||||
mode: 'trigger',
|
||||
triggerId: 'google_forms_webhook',
|
||||
},
|
||||
],
|
||||
|
||||
outputs: {
|
||||
|
||||
@@ -93,6 +93,17 @@ export const hubspotCompanyCreatedTrigger: TriggerConfig = {
|
||||
value: 'hubspot_company_created',
|
||||
},
|
||||
},
|
||||
{
|
||||
id: 'triggerSave',
|
||||
title: '',
|
||||
type: 'trigger-save',
|
||||
mode: 'trigger',
|
||||
triggerId: 'hubspot_company_created',
|
||||
condition: {
|
||||
field: 'selectedTriggerId',
|
||||
value: 'hubspot_company_created',
|
||||
},
|
||||
},
|
||||
{
|
||||
id: 'triggerInstructions',
|
||||
title: 'Setup Instructions',
|
||||
@@ -156,17 +167,6 @@ export const hubspotCompanyCreatedTrigger: TriggerConfig = {
|
||||
value: 'hubspot_company_created',
|
||||
},
|
||||
},
|
||||
{
|
||||
id: 'triggerSave',
|
||||
title: '',
|
||||
type: 'trigger-save',
|
||||
mode: 'trigger',
|
||||
triggerId: 'hubspot_company_created',
|
||||
condition: {
|
||||
field: 'selectedTriggerId',
|
||||
value: 'hubspot_company_created',
|
||||
},
|
||||
},
|
||||
{
|
||||
id: 'samplePayload',
|
||||
title: 'Event Payload Example',
|
||||
|
||||
@@ -93,6 +93,17 @@ export const hubspotCompanyDeletedTrigger: TriggerConfig = {
|
||||
value: 'hubspot_company_deleted',
|
||||
},
|
||||
},
|
||||
{
|
||||
id: 'triggerSave',
|
||||
title: '',
|
||||
type: 'trigger-save',
|
||||
mode: 'trigger',
|
||||
triggerId: 'hubspot_company_deleted',
|
||||
condition: {
|
||||
field: 'selectedTriggerId',
|
||||
value: 'hubspot_company_deleted',
|
||||
},
|
||||
},
|
||||
{
|
||||
id: 'triggerInstructions',
|
||||
title: 'Setup Instructions',
|
||||
@@ -156,17 +167,6 @@ export const hubspotCompanyDeletedTrigger: TriggerConfig = {
|
||||
value: 'hubspot_company_deleted',
|
||||
},
|
||||
},
|
||||
{
|
||||
id: 'triggerSave',
|
||||
title: '',
|
||||
type: 'trigger-save',
|
||||
mode: 'trigger',
|
||||
triggerId: 'hubspot_company_deleted',
|
||||
condition: {
|
||||
field: 'selectedTriggerId',
|
||||
value: 'hubspot_company_deleted',
|
||||
},
|
||||
},
|
||||
{
|
||||
id: 'samplePayload',
|
||||
title: 'Event Payload Example',
|
||||
|
||||
@@ -107,6 +107,17 @@ export const hubspotCompanyPropertyChangedTrigger: TriggerConfig = {
|
||||
value: 'hubspot_company_property_changed',
|
||||
},
|
||||
},
|
||||
{
|
||||
id: 'triggerSave',
|
||||
title: '',
|
||||
type: 'trigger-save',
|
||||
mode: 'trigger',
|
||||
triggerId: 'hubspot_company_property_changed',
|
||||
condition: {
|
||||
field: 'selectedTriggerId',
|
||||
value: 'hubspot_company_property_changed',
|
||||
},
|
||||
},
|
||||
{
|
||||
id: 'triggerInstructions',
|
||||
title: 'Setup Instructions',
|
||||
@@ -170,17 +181,6 @@ export const hubspotCompanyPropertyChangedTrigger: TriggerConfig = {
|
||||
value: 'hubspot_company_property_changed',
|
||||
},
|
||||
},
|
||||
{
|
||||
id: 'triggerSave',
|
||||
title: '',
|
||||
type: 'trigger-save',
|
||||
mode: 'trigger',
|
||||
triggerId: 'hubspot_company_property_changed',
|
||||
condition: {
|
||||
field: 'selectedTriggerId',
|
||||
value: 'hubspot_company_property_changed',
|
||||
},
|
||||
},
|
||||
{
|
||||
id: 'samplePayload',
|
||||
title: 'Event Payload Example',
|
||||
|
||||
@@ -93,6 +93,17 @@ export const hubspotContactCreatedTrigger: TriggerConfig = {
|
||||
value: 'hubspot_contact_created',
|
||||
},
|
||||
},
|
||||
{
|
||||
id: 'triggerSave',
|
||||
title: '',
|
||||
type: 'trigger-save',
|
||||
mode: 'trigger',
|
||||
triggerId: 'hubspot_contact_created',
|
||||
condition: {
|
||||
field: 'selectedTriggerId',
|
||||
value: 'hubspot_contact_created',
|
||||
},
|
||||
},
|
||||
{
|
||||
id: 'triggerInstructions',
|
||||
title: 'Setup Instructions',
|
||||
@@ -156,17 +167,6 @@ export const hubspotContactCreatedTrigger: TriggerConfig = {
|
||||
value: 'hubspot_contact_created',
|
||||
},
|
||||
},
|
||||
{
|
||||
id: 'triggerSave',
|
||||
title: '',
|
||||
type: 'trigger-save',
|
||||
mode: 'trigger',
|
||||
triggerId: 'hubspot_contact_created',
|
||||
condition: {
|
||||
field: 'selectedTriggerId',
|
||||
value: 'hubspot_contact_created',
|
||||
},
|
||||
},
|
||||
{
|
||||
id: 'samplePayload',
|
||||
title: 'Event Payload Example',
|
||||
|
||||
@@ -93,6 +93,17 @@ export const hubspotContactDeletedTrigger: TriggerConfig = {
|
||||
value: 'hubspot_contact_deleted',
|
||||
},
|
||||
},
|
||||
{
|
||||
id: 'triggerSave',
|
||||
title: '',
|
||||
type: 'trigger-save',
|
||||
mode: 'trigger',
|
||||
triggerId: 'hubspot_contact_deleted',
|
||||
condition: {
|
||||
field: 'selectedTriggerId',
|
||||
value: 'hubspot_contact_deleted',
|
||||
},
|
||||
},
|
||||
{
|
||||
id: 'triggerInstructions',
|
||||
title: 'Setup Instructions',
|
||||
@@ -156,17 +167,6 @@ export const hubspotContactDeletedTrigger: TriggerConfig = {
|
||||
value: 'hubspot_contact_deleted',
|
||||
},
|
||||
},
|
||||
{
|
||||
id: 'triggerSave',
|
||||
title: '',
|
||||
type: 'trigger-save',
|
||||
mode: 'trigger',
|
||||
triggerId: 'hubspot_contact_deleted',
|
||||
condition: {
|
||||
field: 'selectedTriggerId',
|
||||
value: 'hubspot_contact_deleted',
|
||||
},
|
||||
},
|
||||
{
|
||||
id: 'samplePayload',
|
||||
title: 'Event Payload Example',
|
||||
|
||||
@@ -94,6 +94,17 @@ export const hubspotContactPrivacyDeletedTrigger: TriggerConfig = {
|
||||
value: 'hubspot_contact_privacy_deleted',
|
||||
},
|
||||
},
|
||||
{
|
||||
id: 'triggerSave',
|
||||
title: '',
|
||||
type: 'trigger-save',
|
||||
mode: 'trigger',
|
||||
triggerId: 'hubspot_contact_privacy_deleted',
|
||||
condition: {
|
||||
field: 'selectedTriggerId',
|
||||
value: 'hubspot_contact_privacy_deleted',
|
||||
},
|
||||
},
|
||||
{
|
||||
id: 'triggerInstructions',
|
||||
title: 'Setup Instructions',
|
||||
@@ -157,17 +168,6 @@ export const hubspotContactPrivacyDeletedTrigger: TriggerConfig = {
|
||||
value: 'hubspot_contact_privacy_deleted',
|
||||
},
|
||||
},
|
||||
{
|
||||
id: 'triggerSave',
|
||||
title: '',
|
||||
type: 'trigger-save',
|
||||
mode: 'trigger',
|
||||
triggerId: 'hubspot_contact_privacy_deleted',
|
||||
condition: {
|
||||
field: 'selectedTriggerId',
|
||||
value: 'hubspot_contact_privacy_deleted',
|
||||
},
|
||||
},
|
||||
{
|
||||
id: 'samplePayload',
|
||||
title: 'Event Payload Example',
|
||||
|
||||
@@ -107,6 +107,17 @@ export const hubspotContactPropertyChangedTrigger: TriggerConfig = {
|
||||
value: 'hubspot_contact_property_changed',
|
||||
},
|
||||
},
|
||||
{
|
||||
id: 'triggerSave',
|
||||
title: '',
|
||||
type: 'trigger-save',
|
||||
mode: 'trigger',
|
||||
triggerId: 'hubspot_contact_property_changed',
|
||||
condition: {
|
||||
field: 'selectedTriggerId',
|
||||
value: 'hubspot_contact_property_changed',
|
||||
},
|
||||
},
|
||||
{
|
||||
id: 'triggerInstructions',
|
||||
title: 'Setup Instructions',
|
||||
@@ -170,17 +181,6 @@ export const hubspotContactPropertyChangedTrigger: TriggerConfig = {
|
||||
value: 'hubspot_contact_property_changed',
|
||||
},
|
||||
},
|
||||
{
|
||||
id: 'triggerSave',
|
||||
title: '',
|
||||
type: 'trigger-save',
|
||||
mode: 'trigger',
|
||||
triggerId: 'hubspot_contact_property_changed',
|
||||
condition: {
|
||||
field: 'selectedTriggerId',
|
||||
value: 'hubspot_contact_property_changed',
|
||||
},
|
||||
},
|
||||
{
|
||||
id: 'samplePayload',
|
||||
title: 'Event Payload Example',
|
||||
|
||||
@@ -93,6 +93,17 @@ export const hubspotConversationCreationTrigger: TriggerConfig = {
|
||||
value: 'hubspot_conversation_creation',
|
||||
},
|
||||
},
|
||||
{
|
||||
id: 'triggerSave',
|
||||
title: '',
|
||||
type: 'trigger-save',
|
||||
mode: 'trigger',
|
||||
triggerId: 'hubspot_conversation_creation',
|
||||
condition: {
|
||||
field: 'selectedTriggerId',
|
||||
value: 'hubspot_conversation_creation',
|
||||
},
|
||||
},
|
||||
{
|
||||
id: 'triggerInstructions',
|
||||
title: 'Setup Instructions',
|
||||
@@ -156,17 +167,6 @@ export const hubspotConversationCreationTrigger: TriggerConfig = {
|
||||
value: 'hubspot_conversation_creation',
|
||||
},
|
||||
},
|
||||
{
|
||||
id: 'triggerSave',
|
||||
title: '',
|
||||
type: 'trigger-save',
|
||||
mode: 'trigger',
|
||||
triggerId: 'hubspot_conversation_creation',
|
||||
condition: {
|
||||
field: 'selectedTriggerId',
|
||||
value: 'hubspot_conversation_creation',
|
||||
},
|
||||
},
|
||||
{
|
||||
id: 'samplePayload',
|
||||
title: 'Event Payload Example',
|
||||
|
||||
@@ -93,6 +93,17 @@ export const hubspotConversationDeletionTrigger: TriggerConfig = {
|
||||
value: 'hubspot_conversation_deletion',
|
||||
},
|
||||
},
|
||||
{
|
||||
id: 'triggerSave',
|
||||
title: '',
|
||||
type: 'trigger-save',
|
||||
mode: 'trigger',
|
||||
triggerId: 'hubspot_conversation_deletion',
|
||||
condition: {
|
||||
field: 'selectedTriggerId',
|
||||
value: 'hubspot_conversation_deletion',
|
||||
},
|
||||
},
|
||||
{
|
||||
id: 'triggerInstructions',
|
||||
title: 'Setup Instructions',
|
||||
@@ -156,17 +167,6 @@ export const hubspotConversationDeletionTrigger: TriggerConfig = {
|
||||
value: 'hubspot_conversation_deletion',
|
||||
},
|
||||
},
|
||||
{
|
||||
id: 'triggerSave',
|
||||
title: '',
|
||||
type: 'trigger-save',
|
||||
mode: 'trigger',
|
||||
triggerId: 'hubspot_conversation_deletion',
|
||||
condition: {
|
||||
field: 'selectedTriggerId',
|
||||
value: 'hubspot_conversation_deletion',
|
||||
},
|
||||
},
|
||||
{
|
||||
id: 'samplePayload',
|
||||
title: 'Event Payload Example',
|
||||
|
||||
@@ -93,6 +93,17 @@ export const hubspotConversationNewMessageTrigger: TriggerConfig = {
|
||||
value: 'hubspot_conversation_new_message',
|
||||
},
|
||||
},
|
||||
{
|
||||
id: 'triggerSave',
|
||||
title: '',
|
||||
type: 'trigger-save',
|
||||
mode: 'trigger',
|
||||
triggerId: 'hubspot_conversation_new_message',
|
||||
condition: {
|
||||
field: 'selectedTriggerId',
|
||||
value: 'hubspot_conversation_new_message',
|
||||
},
|
||||
},
|
||||
{
|
||||
id: 'triggerInstructions',
|
||||
title: 'Setup Instructions',
|
||||
@@ -156,17 +167,6 @@ export const hubspotConversationNewMessageTrigger: TriggerConfig = {
|
||||
value: 'hubspot_conversation_new_message',
|
||||
},
|
||||
},
|
||||
{
|
||||
id: 'triggerSave',
|
||||
title: '',
|
||||
type: 'trigger-save',
|
||||
mode: 'trigger',
|
||||
triggerId: 'hubspot_conversation_new_message',
|
||||
condition: {
|
||||
field: 'selectedTriggerId',
|
||||
value: 'hubspot_conversation_new_message',
|
||||
},
|
||||
},
|
||||
{
|
||||
id: 'samplePayload',
|
||||
title: 'Event Payload Example',
|
||||
|
||||
@@ -94,6 +94,17 @@ export const hubspotConversationPrivacyDeletionTrigger: TriggerConfig = {
|
||||
value: 'hubspot_conversation_privacy_deletion',
|
||||
},
|
||||
},
|
||||
{
|
||||
id: 'triggerSave',
|
||||
title: '',
|
||||
type: 'trigger-save',
|
||||
mode: 'trigger',
|
||||
triggerId: 'hubspot_conversation_privacy_deletion',
|
||||
condition: {
|
||||
field: 'selectedTriggerId',
|
||||
value: 'hubspot_conversation_privacy_deletion',
|
||||
},
|
||||
},
|
||||
{
|
||||
id: 'triggerInstructions',
|
||||
title: 'Setup Instructions',
|
||||
@@ -157,17 +168,6 @@ export const hubspotConversationPrivacyDeletionTrigger: TriggerConfig = {
|
||||
value: 'hubspot_conversation_privacy_deletion',
|
||||
},
|
||||
},
|
||||
{
|
||||
id: 'triggerSave',
|
||||
title: '',
|
||||
type: 'trigger-save',
|
||||
mode: 'trigger',
|
||||
triggerId: 'hubspot_conversation_privacy_deletion',
|
||||
condition: {
|
||||
field: 'selectedTriggerId',
|
||||
value: 'hubspot_conversation_privacy_deletion',
|
||||
},
|
||||
},
|
||||
{
|
||||
id: 'samplePayload',
|
||||
title: 'Event Payload Example',
|
||||
|
||||
@@ -107,6 +107,17 @@ export const hubspotConversationPropertyChangedTrigger: TriggerConfig = {
|
||||
value: 'hubspot_conversation_property_changed',
|
||||
},
|
||||
},
|
||||
{
|
||||
id: 'triggerSave',
|
||||
title: '',
|
||||
type: 'trigger-save',
|
||||
mode: 'trigger',
|
||||
triggerId: 'hubspot_conversation_property_changed',
|
||||
condition: {
|
||||
field: 'selectedTriggerId',
|
||||
value: 'hubspot_conversation_property_changed',
|
||||
},
|
||||
},
|
||||
{
|
||||
id: 'triggerInstructions',
|
||||
title: 'Setup Instructions',
|
||||
@@ -170,17 +181,6 @@ export const hubspotConversationPropertyChangedTrigger: TriggerConfig = {
|
||||
value: 'hubspot_conversation_property_changed',
|
||||
},
|
||||
},
|
||||
{
|
||||
id: 'triggerSave',
|
||||
title: '',
|
||||
type: 'trigger-save',
|
||||
mode: 'trigger',
|
||||
triggerId: 'hubspot_conversation_property_changed',
|
||||
condition: {
|
||||
field: 'selectedTriggerId',
|
||||
value: 'hubspot_conversation_property_changed',
|
||||
},
|
||||
},
|
||||
{
|
||||
id: 'samplePayload',
|
||||
title: 'Event Payload Example',
|
||||
|
||||
@@ -93,6 +93,17 @@ export const hubspotDealCreatedTrigger: TriggerConfig = {
|
||||
value: 'hubspot_deal_created',
|
||||
},
|
||||
},
|
||||
{
|
||||
id: 'triggerSave',
|
||||
title: '',
|
||||
type: 'trigger-save',
|
||||
mode: 'trigger',
|
||||
triggerId: 'hubspot_deal_created',
|
||||
condition: {
|
||||
field: 'selectedTriggerId',
|
||||
value: 'hubspot_deal_created',
|
||||
},
|
||||
},
|
||||
{
|
||||
id: 'triggerInstructions',
|
||||
title: 'Setup Instructions',
|
||||
@@ -156,17 +167,6 @@ export const hubspotDealCreatedTrigger: TriggerConfig = {
|
||||
value: 'hubspot_deal_created',
|
||||
},
|
||||
},
|
||||
{
|
||||
id: 'triggerSave',
|
||||
title: '',
|
||||
type: 'trigger-save',
|
||||
mode: 'trigger',
|
||||
triggerId: 'hubspot_deal_created',
|
||||
condition: {
|
||||
field: 'selectedTriggerId',
|
||||
value: 'hubspot_deal_created',
|
||||
},
|
||||
},
|
||||
{
|
||||
id: 'samplePayload',
|
||||
title: 'Event Payload Example',
|
||||
|
||||
@@ -93,6 +93,17 @@ export const hubspotDealDeletedTrigger: TriggerConfig = {
|
||||
value: 'hubspot_deal_deleted',
|
||||
},
|
||||
},
|
||||
{
|
||||
id: 'triggerSave',
|
||||
title: '',
|
||||
type: 'trigger-save',
|
||||
mode: 'trigger',
|
||||
triggerId: 'hubspot_deal_deleted',
|
||||
condition: {
|
||||
field: 'selectedTriggerId',
|
||||
value: 'hubspot_deal_deleted',
|
||||
},
|
||||
},
|
||||
{
|
||||
id: 'triggerInstructions',
|
||||
title: 'Setup Instructions',
|
||||
@@ -156,17 +167,6 @@ export const hubspotDealDeletedTrigger: TriggerConfig = {
|
||||
value: 'hubspot_deal_deleted',
|
||||
},
|
||||
},
|
||||
{
|
||||
id: 'triggerSave',
|
||||
title: '',
|
||||
type: 'trigger-save',
|
||||
mode: 'trigger',
|
||||
triggerId: 'hubspot_deal_deleted',
|
||||
condition: {
|
||||
field: 'selectedTriggerId',
|
||||
value: 'hubspot_deal_deleted',
|
||||
},
|
||||
},
|
||||
{
|
||||
id: 'samplePayload',
|
||||
title: 'Event Payload Example',
|
||||
|
||||
@@ -107,6 +107,17 @@ export const hubspotDealPropertyChangedTrigger: TriggerConfig = {
|
||||
value: 'hubspot_deal_property_changed',
|
||||
},
|
||||
},
|
||||
{
|
||||
id: 'triggerSave',
|
||||
title: '',
|
||||
type: 'trigger-save',
|
||||
mode: 'trigger',
|
||||
triggerId: 'hubspot_deal_property_changed',
|
||||
condition: {
|
||||
field: 'selectedTriggerId',
|
||||
value: 'hubspot_deal_property_changed',
|
||||
},
|
||||
},
|
||||
{
|
||||
id: 'triggerInstructions',
|
||||
title: 'Setup Instructions',
|
||||
@@ -170,17 +181,6 @@ export const hubspotDealPropertyChangedTrigger: TriggerConfig = {
|
||||
value: 'hubspot_deal_property_changed',
|
||||
},
|
||||
},
|
||||
{
|
||||
id: 'triggerSave',
|
||||
title: '',
|
||||
type: 'trigger-save',
|
||||
mode: 'trigger',
|
||||
triggerId: 'hubspot_deal_property_changed',
|
||||
condition: {
|
||||
field: 'selectedTriggerId',
|
||||
value: 'hubspot_deal_property_changed',
|
||||
},
|
||||
},
|
||||
{
|
||||
id: 'samplePayload',
|
||||
title: 'Event Payload Example',
|
||||
|
||||
@@ -93,6 +93,17 @@ export const hubspotTicketCreatedTrigger: TriggerConfig = {
|
||||
value: 'hubspot_ticket_created',
|
||||
},
|
||||
},
|
||||
{
|
||||
id: 'triggerSave',
|
||||
title: '',
|
||||
type: 'trigger-save',
|
||||
mode: 'trigger',
|
||||
triggerId: 'hubspot_ticket_created',
|
||||
condition: {
|
||||
field: 'selectedTriggerId',
|
||||
value: 'hubspot_ticket_created',
|
||||
},
|
||||
},
|
||||
{
|
||||
id: 'triggerInstructions',
|
||||
title: 'Setup Instructions',
|
||||
@@ -156,17 +167,6 @@ export const hubspotTicketCreatedTrigger: TriggerConfig = {
|
||||
value: 'hubspot_ticket_created',
|
||||
},
|
||||
},
|
||||
{
|
||||
id: 'triggerSave',
|
||||
title: '',
|
||||
type: 'trigger-save',
|
||||
mode: 'trigger',
|
||||
triggerId: 'hubspot_ticket_created',
|
||||
condition: {
|
||||
field: 'selectedTriggerId',
|
||||
value: 'hubspot_ticket_created',
|
||||
},
|
||||
},
|
||||
{
|
||||
id: 'samplePayload',
|
||||
title: 'Event Payload Example',
|
||||
|
||||
@@ -93,6 +93,17 @@ export const hubspotTicketDeletedTrigger: TriggerConfig = {
|
||||
value: 'hubspot_ticket_deleted',
|
||||
},
|
||||
},
|
||||
{
|
||||
id: 'triggerSave',
|
||||
title: '',
|
||||
type: 'trigger-save',
|
||||
mode: 'trigger',
|
||||
triggerId: 'hubspot_ticket_deleted',
|
||||
condition: {
|
||||
field: 'selectedTriggerId',
|
||||
value: 'hubspot_ticket_deleted',
|
||||
},
|
||||
},
|
||||
{
|
||||
id: 'triggerInstructions',
|
||||
title: 'Setup Instructions',
|
||||
@@ -156,17 +167,6 @@ export const hubspotTicketDeletedTrigger: TriggerConfig = {
|
||||
value: 'hubspot_ticket_deleted',
|
||||
},
|
||||
},
|
||||
{
|
||||
id: 'triggerSave',
|
||||
title: '',
|
||||
type: 'trigger-save',
|
||||
mode: 'trigger',
|
||||
triggerId: 'hubspot_ticket_deleted',
|
||||
condition: {
|
||||
field: 'selectedTriggerId',
|
||||
value: 'hubspot_ticket_deleted',
|
||||
},
|
||||
},
|
||||
{
|
||||
id: 'samplePayload',
|
||||
title: 'Event Payload Example',
|
||||
|
||||
@@ -107,6 +107,17 @@ export const hubspotTicketPropertyChangedTrigger: TriggerConfig = {
|
||||
value: 'hubspot_ticket_property_changed',
|
||||
},
|
||||
},
|
||||
{
|
||||
id: 'triggerSave',
|
||||
title: '',
|
||||
type: 'trigger-save',
|
||||
mode: 'trigger',
|
||||
triggerId: 'hubspot_ticket_property_changed',
|
||||
condition: {
|
||||
field: 'selectedTriggerId',
|
||||
value: 'hubspot_ticket_property_changed',
|
||||
},
|
||||
},
|
||||
{
|
||||
id: 'triggerInstructions',
|
||||
title: 'Setup Instructions',
|
||||
@@ -170,17 +181,6 @@ export const hubspotTicketPropertyChangedTrigger: TriggerConfig = {
|
||||
value: 'hubspot_ticket_property_changed',
|
||||
},
|
||||
},
|
||||
{
|
||||
id: 'triggerSave',
|
||||
title: '',
|
||||
type: 'trigger-save',
|
||||
mode: 'trigger',
|
||||
triggerId: 'hubspot_ticket_property_changed',
|
||||
condition: {
|
||||
field: 'selectedTriggerId',
|
||||
value: 'hubspot_ticket_property_changed',
|
||||
},
|
||||
},
|
||||
{
|
||||
id: 'samplePayload',
|
||||
title: 'Event Payload Example',
|
||||
|
||||
@@ -82,7 +82,7 @@ export function hubspotSetupInstructions(eventType: string, additionalNotes?: st
|
||||
'<strong>Step 3: Configure OAuth Settings</strong><br/>After creating your app via CLI, configure it to add the OAuth Redirect URL: <code>https://www.sim.ai/api/auth/oauth2/callback/hubspot</code>. Then retrieve your <strong>Client ID</strong> and <strong>Client Secret</strong> from your app configuration and enter them in the fields above.',
|
||||
"<strong>Step 4: Get App ID and Developer API Key</strong><br/>In your HubSpot developer account, find your <strong>App ID</strong> (shown below your app name) and your <strong>Developer API Key</strong> (in app settings). You'll need both for the next steps.",
|
||||
'<strong>Step 5: Set Required Scopes</strong><br/>Configure your app to include the required OAuth scope: <code>crm.objects.contacts.read</code>',
|
||||
'<strong>Step 6: Save Configuration in Sim</strong><br/>Click the <strong>"Save Configuration"</strong> button below. This will generate your unique webhook URL.',
|
||||
'<strong>Step 6: Save Configuration in Sim</strong><br/>Your unique webhook URL will be generated automatically once you complete the configuration above.',
|
||||
'<strong>Step 7: Configure Webhook in HubSpot via API</strong><br/>After saving above, copy the <strong>Webhook URL</strong> and run the two curl commands below (replace <code>{YOUR_APP_ID}</code>, <code>{YOUR_DEVELOPER_API_KEY}</code>, and <code>{YOUR_WEBHOOK_URL_FROM_ABOVE}</code> with your actual values).',
|
||||
"<strong>Step 8: Test Your Webhook</strong><br/>Create or modify a contact in HubSpot to trigger the webhook. Check your workflow execution logs in Sim to verify it's working.",
|
||||
]
|
||||
|
||||
@@ -56,18 +56,6 @@ export const jiraIssueCommentedTrigger: TriggerConfig = {
|
||||
value: 'jira_issue_commented',
|
||||
},
|
||||
},
|
||||
{
|
||||
id: 'triggerInstructions',
|
||||
title: 'Setup Instructions',
|
||||
hideFromPreview: true,
|
||||
type: 'text',
|
||||
defaultValue: jiraSetupInstructions('comment_created'),
|
||||
mode: 'trigger',
|
||||
condition: {
|
||||
field: 'selectedTriggerId',
|
||||
value: 'jira_issue_commented',
|
||||
},
|
||||
},
|
||||
{
|
||||
id: 'triggerSave',
|
||||
title: '',
|
||||
@@ -80,6 +68,18 @@ export const jiraIssueCommentedTrigger: TriggerConfig = {
|
||||
value: 'jira_issue_commented',
|
||||
},
|
||||
},
|
||||
{
|
||||
id: 'triggerInstructions',
|
||||
title: 'Setup Instructions',
|
||||
hideFromPreview: true,
|
||||
type: 'text',
|
||||
defaultValue: jiraSetupInstructions('comment_created'),
|
||||
mode: 'trigger',
|
||||
condition: {
|
||||
field: 'selectedTriggerId',
|
||||
value: 'jira_issue_commented',
|
||||
},
|
||||
},
|
||||
],
|
||||
|
||||
outputs: buildCommentOutputs(),
|
||||
|
||||
@@ -65,18 +65,6 @@ export const jiraIssueCreatedTrigger: TriggerConfig = {
|
||||
value: 'jira_issue_created',
|
||||
},
|
||||
},
|
||||
{
|
||||
id: 'triggerInstructions',
|
||||
title: 'Setup Instructions',
|
||||
hideFromPreview: true,
|
||||
type: 'text',
|
||||
defaultValue: jiraSetupInstructions('jira:issue_created'),
|
||||
mode: 'trigger',
|
||||
condition: {
|
||||
field: 'selectedTriggerId',
|
||||
value: 'jira_issue_created',
|
||||
},
|
||||
},
|
||||
{
|
||||
id: 'triggerSave',
|
||||
title: '',
|
||||
@@ -89,6 +77,18 @@ export const jiraIssueCreatedTrigger: TriggerConfig = {
|
||||
value: 'jira_issue_created',
|
||||
},
|
||||
},
|
||||
{
|
||||
id: 'triggerInstructions',
|
||||
title: 'Setup Instructions',
|
||||
hideFromPreview: true,
|
||||
type: 'text',
|
||||
defaultValue: jiraSetupInstructions('jira:issue_created'),
|
||||
mode: 'trigger',
|
||||
condition: {
|
||||
field: 'selectedTriggerId',
|
||||
value: 'jira_issue_created',
|
||||
},
|
||||
},
|
||||
],
|
||||
|
||||
outputs: buildIssueOutputs(),
|
||||
|
||||
@@ -56,18 +56,6 @@ export const jiraIssueDeletedTrigger: TriggerConfig = {
|
||||
value: 'jira_issue_deleted',
|
||||
},
|
||||
},
|
||||
{
|
||||
id: 'triggerInstructions',
|
||||
title: 'Setup Instructions',
|
||||
hideFromPreview: true,
|
||||
type: 'text',
|
||||
defaultValue: jiraSetupInstructions('jira:issue_deleted'),
|
||||
mode: 'trigger',
|
||||
condition: {
|
||||
field: 'selectedTriggerId',
|
||||
value: 'jira_issue_deleted',
|
||||
},
|
||||
},
|
||||
{
|
||||
id: 'triggerSave',
|
||||
title: '',
|
||||
@@ -80,6 +68,18 @@ export const jiraIssueDeletedTrigger: TriggerConfig = {
|
||||
value: 'jira_issue_deleted',
|
||||
},
|
||||
},
|
||||
{
|
||||
id: 'triggerInstructions',
|
||||
title: 'Setup Instructions',
|
||||
hideFromPreview: true,
|
||||
type: 'text',
|
||||
defaultValue: jiraSetupInstructions('jira:issue_deleted'),
|
||||
mode: 'trigger',
|
||||
condition: {
|
||||
field: 'selectedTriggerId',
|
||||
value: 'jira_issue_deleted',
|
||||
},
|
||||
},
|
||||
],
|
||||
|
||||
outputs: buildIssueOutputs(),
|
||||
|
||||
@@ -70,18 +70,6 @@ export const jiraIssueUpdatedTrigger: TriggerConfig = {
|
||||
value: 'jira_issue_updated',
|
||||
},
|
||||
},
|
||||
{
|
||||
id: 'triggerInstructions',
|
||||
title: 'Setup Instructions',
|
||||
hideFromPreview: true,
|
||||
type: 'text',
|
||||
defaultValue: jiraSetupInstructions('jira:issue_updated'),
|
||||
mode: 'trigger',
|
||||
condition: {
|
||||
field: 'selectedTriggerId',
|
||||
value: 'jira_issue_updated',
|
||||
},
|
||||
},
|
||||
{
|
||||
id: 'triggerSave',
|
||||
title: '',
|
||||
@@ -94,6 +82,18 @@ export const jiraIssueUpdatedTrigger: TriggerConfig = {
|
||||
value: 'jira_issue_updated',
|
||||
},
|
||||
},
|
||||
{
|
||||
id: 'triggerInstructions',
|
||||
title: 'Setup Instructions',
|
||||
hideFromPreview: true,
|
||||
type: 'text',
|
||||
defaultValue: jiraSetupInstructions('jira:issue_updated'),
|
||||
mode: 'trigger',
|
||||
condition: {
|
||||
field: 'selectedTriggerId',
|
||||
value: 'jira_issue_updated',
|
||||
},
|
||||
},
|
||||
],
|
||||
|
||||
outputs: buildIssueUpdatedOutputs(),
|
||||
|
||||
@@ -43,18 +43,6 @@ export const jiraWebhookTrigger: TriggerConfig = {
|
||||
value: 'jira_webhook',
|
||||
},
|
||||
},
|
||||
{
|
||||
id: 'triggerInstructions',
|
||||
title: 'Setup Instructions',
|
||||
hideFromPreview: true,
|
||||
type: 'text',
|
||||
defaultValue: jiraSetupInstructions('All Events'),
|
||||
mode: 'trigger',
|
||||
condition: {
|
||||
field: 'selectedTriggerId',
|
||||
value: 'jira_webhook',
|
||||
},
|
||||
},
|
||||
{
|
||||
id: 'triggerSave',
|
||||
title: '',
|
||||
@@ -67,6 +55,18 @@ export const jiraWebhookTrigger: TriggerConfig = {
|
||||
value: 'jira_webhook',
|
||||
},
|
||||
},
|
||||
{
|
||||
id: 'triggerInstructions',
|
||||
title: 'Setup Instructions',
|
||||
hideFromPreview: true,
|
||||
type: 'text',
|
||||
defaultValue: jiraSetupInstructions('All Events'),
|
||||
mode: 'trigger',
|
||||
condition: {
|
||||
field: 'selectedTriggerId',
|
||||
value: 'jira_webhook',
|
||||
},
|
||||
},
|
||||
],
|
||||
|
||||
outputs: {
|
||||
|
||||
@@ -56,18 +56,6 @@ export const jiraWorklogCreatedTrigger: TriggerConfig = {
|
||||
value: 'jira_worklog_created',
|
||||
},
|
||||
},
|
||||
{
|
||||
id: 'triggerInstructions',
|
||||
title: 'Setup Instructions',
|
||||
hideFromPreview: true,
|
||||
type: 'text',
|
||||
defaultValue: jiraSetupInstructions('worklog_created'),
|
||||
mode: 'trigger',
|
||||
condition: {
|
||||
field: 'selectedTriggerId',
|
||||
value: 'jira_worklog_created',
|
||||
},
|
||||
},
|
||||
{
|
||||
id: 'triggerSave',
|
||||
title: '',
|
||||
@@ -80,6 +68,18 @@ export const jiraWorklogCreatedTrigger: TriggerConfig = {
|
||||
value: 'jira_worklog_created',
|
||||
},
|
||||
},
|
||||
{
|
||||
id: 'triggerInstructions',
|
||||
title: 'Setup Instructions',
|
||||
hideFromPreview: true,
|
||||
type: 'text',
|
||||
defaultValue: jiraSetupInstructions('worklog_created'),
|
||||
mode: 'trigger',
|
||||
condition: {
|
||||
field: 'selectedTriggerId',
|
||||
value: 'jira_worklog_created',
|
||||
},
|
||||
},
|
||||
],
|
||||
|
||||
outputs: buildWorklogOutputs(),
|
||||
|
||||
@@ -39,18 +39,6 @@ export const linearCommentCreatedTrigger: TriggerConfig = {
|
||||
value: 'linear_comment_created',
|
||||
},
|
||||
},
|
||||
{
|
||||
id: 'triggerInstructions',
|
||||
title: 'Setup Instructions',
|
||||
hideFromPreview: true,
|
||||
type: 'text',
|
||||
defaultValue: linearSetupInstructions('Comment (create)'),
|
||||
mode: 'trigger',
|
||||
condition: {
|
||||
field: 'selectedTriggerId',
|
||||
value: 'linear_comment_created',
|
||||
},
|
||||
},
|
||||
{
|
||||
id: 'triggerSave',
|
||||
title: '',
|
||||
@@ -63,6 +51,18 @@ export const linearCommentCreatedTrigger: TriggerConfig = {
|
||||
value: 'linear_comment_created',
|
||||
},
|
||||
},
|
||||
{
|
||||
id: 'triggerInstructions',
|
||||
title: 'Setup Instructions',
|
||||
hideFromPreview: true,
|
||||
type: 'text',
|
||||
defaultValue: linearSetupInstructions('Comment (create)'),
|
||||
mode: 'trigger',
|
||||
condition: {
|
||||
field: 'selectedTriggerId',
|
||||
value: 'linear_comment_created',
|
||||
},
|
||||
},
|
||||
],
|
||||
|
||||
outputs: buildCommentOutputs(),
|
||||
|
||||
@@ -39,18 +39,6 @@ export const linearCommentUpdatedTrigger: TriggerConfig = {
|
||||
value: 'linear_comment_updated',
|
||||
},
|
||||
},
|
||||
{
|
||||
id: 'triggerInstructions',
|
||||
title: 'Setup Instructions',
|
||||
hideFromPreview: true,
|
||||
type: 'text',
|
||||
defaultValue: linearSetupInstructions('Comment (update)'),
|
||||
mode: 'trigger',
|
||||
condition: {
|
||||
field: 'selectedTriggerId',
|
||||
value: 'linear_comment_updated',
|
||||
},
|
||||
},
|
||||
{
|
||||
id: 'triggerSave',
|
||||
title: '',
|
||||
@@ -63,6 +51,18 @@ export const linearCommentUpdatedTrigger: TriggerConfig = {
|
||||
value: 'linear_comment_updated',
|
||||
},
|
||||
},
|
||||
{
|
||||
id: 'triggerInstructions',
|
||||
title: 'Setup Instructions',
|
||||
hideFromPreview: true,
|
||||
type: 'text',
|
||||
defaultValue: linearSetupInstructions('Comment (update)'),
|
||||
mode: 'trigger',
|
||||
condition: {
|
||||
field: 'selectedTriggerId',
|
||||
value: 'linear_comment_updated',
|
||||
},
|
||||
},
|
||||
],
|
||||
|
||||
outputs: buildCommentOutputs(),
|
||||
|
||||
@@ -39,18 +39,6 @@ export const linearCustomerRequestCreatedTrigger: TriggerConfig = {
|
||||
value: 'linear_customer_request_created',
|
||||
},
|
||||
},
|
||||
{
|
||||
id: 'triggerInstructions',
|
||||
title: 'Setup Instructions',
|
||||
hideFromPreview: true,
|
||||
type: 'text',
|
||||
defaultValue: linearSetupInstructions('Customer Requests'),
|
||||
mode: 'trigger',
|
||||
condition: {
|
||||
field: 'selectedTriggerId',
|
||||
value: 'linear_customer_request_created',
|
||||
},
|
||||
},
|
||||
{
|
||||
id: 'triggerSave',
|
||||
title: '',
|
||||
@@ -63,6 +51,18 @@ export const linearCustomerRequestCreatedTrigger: TriggerConfig = {
|
||||
value: 'linear_customer_request_created',
|
||||
},
|
||||
},
|
||||
{
|
||||
id: 'triggerInstructions',
|
||||
title: 'Setup Instructions',
|
||||
hideFromPreview: true,
|
||||
type: 'text',
|
||||
defaultValue: linearSetupInstructions('Customer Requests'),
|
||||
mode: 'trigger',
|
||||
condition: {
|
||||
field: 'selectedTriggerId',
|
||||
value: 'linear_customer_request_created',
|
||||
},
|
||||
},
|
||||
],
|
||||
|
||||
outputs: buildCustomerRequestOutputs(),
|
||||
|
||||
@@ -39,18 +39,6 @@ export const linearCustomerRequestUpdatedTrigger: TriggerConfig = {
|
||||
value: 'linear_customer_request_updated',
|
||||
},
|
||||
},
|
||||
{
|
||||
id: 'triggerInstructions',
|
||||
title: 'Setup Instructions',
|
||||
hideFromPreview: true,
|
||||
type: 'text',
|
||||
defaultValue: linearSetupInstructions('CustomerNeed (update)'),
|
||||
mode: 'trigger',
|
||||
condition: {
|
||||
field: 'selectedTriggerId',
|
||||
value: 'linear_customer_request_updated',
|
||||
},
|
||||
},
|
||||
{
|
||||
id: 'triggerSave',
|
||||
title: '',
|
||||
@@ -63,6 +51,18 @@ export const linearCustomerRequestUpdatedTrigger: TriggerConfig = {
|
||||
value: 'linear_customer_request_updated',
|
||||
},
|
||||
},
|
||||
{
|
||||
id: 'triggerInstructions',
|
||||
title: 'Setup Instructions',
|
||||
hideFromPreview: true,
|
||||
type: 'text',
|
||||
defaultValue: linearSetupInstructions('CustomerNeed (update)'),
|
||||
mode: 'trigger',
|
||||
condition: {
|
||||
field: 'selectedTriggerId',
|
||||
value: 'linear_customer_request_updated',
|
||||
},
|
||||
},
|
||||
],
|
||||
|
||||
outputs: buildCustomerRequestOutputs(),
|
||||
|
||||
@@ -39,18 +39,6 @@ export const linearCycleCreatedTrigger: TriggerConfig = {
|
||||
value: 'linear_cycle_created',
|
||||
},
|
||||
},
|
||||
{
|
||||
id: 'triggerInstructions',
|
||||
title: 'Setup Instructions',
|
||||
hideFromPreview: true,
|
||||
type: 'text',
|
||||
defaultValue: linearSetupInstructions('Cycle (create)'),
|
||||
mode: 'trigger',
|
||||
condition: {
|
||||
field: 'selectedTriggerId',
|
||||
value: 'linear_cycle_created',
|
||||
},
|
||||
},
|
||||
{
|
||||
id: 'triggerSave',
|
||||
title: '',
|
||||
@@ -63,6 +51,18 @@ export const linearCycleCreatedTrigger: TriggerConfig = {
|
||||
value: 'linear_cycle_created',
|
||||
},
|
||||
},
|
||||
{
|
||||
id: 'triggerInstructions',
|
||||
title: 'Setup Instructions',
|
||||
hideFromPreview: true,
|
||||
type: 'text',
|
||||
defaultValue: linearSetupInstructions('Cycle (create)'),
|
||||
mode: 'trigger',
|
||||
condition: {
|
||||
field: 'selectedTriggerId',
|
||||
value: 'linear_cycle_created',
|
||||
},
|
||||
},
|
||||
],
|
||||
|
||||
outputs: buildCycleOutputs(),
|
||||
|
||||
@@ -39,18 +39,6 @@ export const linearCycleUpdatedTrigger: TriggerConfig = {
|
||||
value: 'linear_cycle_updated',
|
||||
},
|
||||
},
|
||||
{
|
||||
id: 'triggerInstructions',
|
||||
title: 'Setup Instructions',
|
||||
hideFromPreview: true,
|
||||
type: 'text',
|
||||
defaultValue: linearSetupInstructions('Cycle (update)'),
|
||||
mode: 'trigger',
|
||||
condition: {
|
||||
field: 'selectedTriggerId',
|
||||
value: 'linear_cycle_updated',
|
||||
},
|
||||
},
|
||||
{
|
||||
id: 'triggerSave',
|
||||
title: '',
|
||||
@@ -63,6 +51,18 @@ export const linearCycleUpdatedTrigger: TriggerConfig = {
|
||||
value: 'linear_cycle_updated',
|
||||
},
|
||||
},
|
||||
{
|
||||
id: 'triggerInstructions',
|
||||
title: 'Setup Instructions',
|
||||
hideFromPreview: true,
|
||||
type: 'text',
|
||||
defaultValue: linearSetupInstructions('Cycle (update)'),
|
||||
mode: 'trigger',
|
||||
condition: {
|
||||
field: 'selectedTriggerId',
|
||||
value: 'linear_cycle_updated',
|
||||
},
|
||||
},
|
||||
],
|
||||
|
||||
outputs: buildCycleOutputs(),
|
||||
|
||||
@@ -52,18 +52,6 @@ export const linearIssueCreatedTrigger: TriggerConfig = {
|
||||
value: 'linear_issue_created',
|
||||
},
|
||||
},
|
||||
{
|
||||
id: 'triggerInstructions',
|
||||
title: 'Setup Instructions',
|
||||
hideFromPreview: true,
|
||||
type: 'text',
|
||||
defaultValue: linearSetupInstructions('Issue (create)'),
|
||||
mode: 'trigger',
|
||||
condition: {
|
||||
field: 'selectedTriggerId',
|
||||
value: 'linear_issue_created',
|
||||
},
|
||||
},
|
||||
{
|
||||
id: 'triggerSave',
|
||||
title: '',
|
||||
@@ -76,6 +64,18 @@ export const linearIssueCreatedTrigger: TriggerConfig = {
|
||||
value: 'linear_issue_created',
|
||||
},
|
||||
},
|
||||
{
|
||||
id: 'triggerInstructions',
|
||||
title: 'Setup Instructions',
|
||||
hideFromPreview: true,
|
||||
type: 'text',
|
||||
defaultValue: linearSetupInstructions('Issue (create)'),
|
||||
mode: 'trigger',
|
||||
condition: {
|
||||
field: 'selectedTriggerId',
|
||||
value: 'linear_issue_created',
|
||||
},
|
||||
},
|
||||
],
|
||||
|
||||
outputs: buildIssueOutputs(),
|
||||
|
||||
@@ -39,18 +39,6 @@ export const linearIssueRemovedTrigger: TriggerConfig = {
|
||||
value: 'linear_issue_removed',
|
||||
},
|
||||
},
|
||||
{
|
||||
id: 'triggerInstructions',
|
||||
title: 'Setup Instructions',
|
||||
hideFromPreview: true,
|
||||
type: 'text',
|
||||
defaultValue: linearSetupInstructions('Issue (remove)'),
|
||||
mode: 'trigger',
|
||||
condition: {
|
||||
field: 'selectedTriggerId',
|
||||
value: 'linear_issue_removed',
|
||||
},
|
||||
},
|
||||
{
|
||||
id: 'triggerSave',
|
||||
title: '',
|
||||
@@ -63,6 +51,18 @@ export const linearIssueRemovedTrigger: TriggerConfig = {
|
||||
value: 'linear_issue_removed',
|
||||
},
|
||||
},
|
||||
{
|
||||
id: 'triggerInstructions',
|
||||
title: 'Setup Instructions',
|
||||
hideFromPreview: true,
|
||||
type: 'text',
|
||||
defaultValue: linearSetupInstructions('Issue (remove)'),
|
||||
mode: 'trigger',
|
||||
condition: {
|
||||
field: 'selectedTriggerId',
|
||||
value: 'linear_issue_removed',
|
||||
},
|
||||
},
|
||||
],
|
||||
|
||||
outputs: buildIssueOutputs(),
|
||||
|
||||
@@ -39,18 +39,6 @@ export const linearIssueUpdatedTrigger: TriggerConfig = {
|
||||
value: 'linear_issue_updated',
|
||||
},
|
||||
},
|
||||
{
|
||||
id: 'triggerInstructions',
|
||||
title: 'Setup Instructions',
|
||||
hideFromPreview: true,
|
||||
type: 'text',
|
||||
defaultValue: linearSetupInstructions('Issue (update)'),
|
||||
mode: 'trigger',
|
||||
condition: {
|
||||
field: 'selectedTriggerId',
|
||||
value: 'linear_issue_updated',
|
||||
},
|
||||
},
|
||||
{
|
||||
id: 'triggerSave',
|
||||
title: '',
|
||||
@@ -63,6 +51,18 @@ export const linearIssueUpdatedTrigger: TriggerConfig = {
|
||||
value: 'linear_issue_updated',
|
||||
},
|
||||
},
|
||||
{
|
||||
id: 'triggerInstructions',
|
||||
title: 'Setup Instructions',
|
||||
hideFromPreview: true,
|
||||
type: 'text',
|
||||
defaultValue: linearSetupInstructions('Issue (update)'),
|
||||
mode: 'trigger',
|
||||
condition: {
|
||||
field: 'selectedTriggerId',
|
||||
value: 'linear_issue_updated',
|
||||
},
|
||||
},
|
||||
],
|
||||
|
||||
outputs: buildIssueOutputs(),
|
||||
|
||||
@@ -39,18 +39,6 @@ export const linearLabelCreatedTrigger: TriggerConfig = {
|
||||
value: 'linear_label_created',
|
||||
},
|
||||
},
|
||||
{
|
||||
id: 'triggerInstructions',
|
||||
title: 'Setup Instructions',
|
||||
hideFromPreview: true,
|
||||
type: 'text',
|
||||
defaultValue: linearSetupInstructions('IssueLabel (create)'),
|
||||
mode: 'trigger',
|
||||
condition: {
|
||||
field: 'selectedTriggerId',
|
||||
value: 'linear_label_created',
|
||||
},
|
||||
},
|
||||
{
|
||||
id: 'triggerSave',
|
||||
title: '',
|
||||
@@ -63,6 +51,18 @@ export const linearLabelCreatedTrigger: TriggerConfig = {
|
||||
value: 'linear_label_created',
|
||||
},
|
||||
},
|
||||
{
|
||||
id: 'triggerInstructions',
|
||||
title: 'Setup Instructions',
|
||||
hideFromPreview: true,
|
||||
type: 'text',
|
||||
defaultValue: linearSetupInstructions('IssueLabel (create)'),
|
||||
mode: 'trigger',
|
||||
condition: {
|
||||
field: 'selectedTriggerId',
|
||||
value: 'linear_label_created',
|
||||
},
|
||||
},
|
||||
],
|
||||
|
||||
outputs: buildLabelOutputs(),
|
||||
|
||||
@@ -39,18 +39,6 @@ export const linearLabelUpdatedTrigger: TriggerConfig = {
|
||||
value: 'linear_label_updated',
|
||||
},
|
||||
},
|
||||
{
|
||||
id: 'triggerInstructions',
|
||||
title: 'Setup Instructions',
|
||||
hideFromPreview: true,
|
||||
type: 'text',
|
||||
defaultValue: linearSetupInstructions('IssueLabel (update)'),
|
||||
mode: 'trigger',
|
||||
condition: {
|
||||
field: 'selectedTriggerId',
|
||||
value: 'linear_label_updated',
|
||||
},
|
||||
},
|
||||
{
|
||||
id: 'triggerSave',
|
||||
title: '',
|
||||
@@ -63,6 +51,18 @@ export const linearLabelUpdatedTrigger: TriggerConfig = {
|
||||
value: 'linear_label_updated',
|
||||
},
|
||||
},
|
||||
{
|
||||
id: 'triggerInstructions',
|
||||
title: 'Setup Instructions',
|
||||
hideFromPreview: true,
|
||||
type: 'text',
|
||||
defaultValue: linearSetupInstructions('IssueLabel (update)'),
|
||||
mode: 'trigger',
|
||||
condition: {
|
||||
field: 'selectedTriggerId',
|
||||
value: 'linear_label_updated',
|
||||
},
|
||||
},
|
||||
],
|
||||
|
||||
outputs: buildLabelOutputs(),
|
||||
|
||||
@@ -39,18 +39,6 @@ export const linearProjectCreatedTrigger: TriggerConfig = {
|
||||
value: 'linear_project_created',
|
||||
},
|
||||
},
|
||||
{
|
||||
id: 'triggerInstructions',
|
||||
title: 'Setup Instructions',
|
||||
hideFromPreview: true,
|
||||
type: 'text',
|
||||
defaultValue: linearSetupInstructions('Project (create)'),
|
||||
mode: 'trigger',
|
||||
condition: {
|
||||
field: 'selectedTriggerId',
|
||||
value: 'linear_project_created',
|
||||
},
|
||||
},
|
||||
{
|
||||
id: 'triggerSave',
|
||||
title: '',
|
||||
@@ -63,6 +51,18 @@ export const linearProjectCreatedTrigger: TriggerConfig = {
|
||||
value: 'linear_project_created',
|
||||
},
|
||||
},
|
||||
{
|
||||
id: 'triggerInstructions',
|
||||
title: 'Setup Instructions',
|
||||
hideFromPreview: true,
|
||||
type: 'text',
|
||||
defaultValue: linearSetupInstructions('Project (create)'),
|
||||
mode: 'trigger',
|
||||
condition: {
|
||||
field: 'selectedTriggerId',
|
||||
value: 'linear_project_created',
|
||||
},
|
||||
},
|
||||
],
|
||||
|
||||
outputs: buildProjectOutputs(),
|
||||
|
||||
@@ -39,18 +39,6 @@ export const linearProjectUpdateCreatedTrigger: TriggerConfig = {
|
||||
value: 'linear_project_update_created',
|
||||
},
|
||||
},
|
||||
{
|
||||
id: 'triggerInstructions',
|
||||
title: 'Setup Instructions',
|
||||
hideFromPreview: true,
|
||||
type: 'text',
|
||||
defaultValue: linearSetupInstructions('ProjectUpdate (create)'),
|
||||
mode: 'trigger',
|
||||
condition: {
|
||||
field: 'selectedTriggerId',
|
||||
value: 'linear_project_update_created',
|
||||
},
|
||||
},
|
||||
{
|
||||
id: 'triggerSave',
|
||||
title: '',
|
||||
@@ -63,6 +51,18 @@ export const linearProjectUpdateCreatedTrigger: TriggerConfig = {
|
||||
value: 'linear_project_update_created',
|
||||
},
|
||||
},
|
||||
{
|
||||
id: 'triggerInstructions',
|
||||
title: 'Setup Instructions',
|
||||
hideFromPreview: true,
|
||||
type: 'text',
|
||||
defaultValue: linearSetupInstructions('ProjectUpdate (create)'),
|
||||
mode: 'trigger',
|
||||
condition: {
|
||||
field: 'selectedTriggerId',
|
||||
value: 'linear_project_update_created',
|
||||
},
|
||||
},
|
||||
],
|
||||
|
||||
outputs: buildProjectUpdateOutputs(),
|
||||
|
||||
@@ -39,18 +39,6 @@ export const linearProjectUpdatedTrigger: TriggerConfig = {
|
||||
value: 'linear_project_updated',
|
||||
},
|
||||
},
|
||||
{
|
||||
id: 'triggerInstructions',
|
||||
title: 'Setup Instructions',
|
||||
hideFromPreview: true,
|
||||
type: 'text',
|
||||
defaultValue: linearSetupInstructions('Project (update)'),
|
||||
mode: 'trigger',
|
||||
condition: {
|
||||
field: 'selectedTriggerId',
|
||||
value: 'linear_project_updated',
|
||||
},
|
||||
},
|
||||
{
|
||||
id: 'triggerSave',
|
||||
title: '',
|
||||
@@ -63,6 +51,18 @@ export const linearProjectUpdatedTrigger: TriggerConfig = {
|
||||
value: 'linear_project_updated',
|
||||
},
|
||||
},
|
||||
{
|
||||
id: 'triggerInstructions',
|
||||
title: 'Setup Instructions',
|
||||
hideFromPreview: true,
|
||||
type: 'text',
|
||||
defaultValue: linearSetupInstructions('Project (update)'),
|
||||
mode: 'trigger',
|
||||
condition: {
|
||||
field: 'selectedTriggerId',
|
||||
value: 'linear_project_updated',
|
||||
},
|
||||
},
|
||||
],
|
||||
|
||||
outputs: buildProjectOutputs(),
|
||||
|
||||
@@ -39,6 +39,18 @@ export const linearWebhookTrigger: TriggerConfig = {
|
||||
value: 'linear_webhook',
|
||||
},
|
||||
},
|
||||
{
|
||||
id: 'triggerSave',
|
||||
title: '',
|
||||
type: 'trigger-save',
|
||||
hideFromPreview: true,
|
||||
mode: 'trigger',
|
||||
triggerId: 'linear_webhook',
|
||||
condition: {
|
||||
field: 'selectedTriggerId',
|
||||
value: 'linear_webhook',
|
||||
},
|
||||
},
|
||||
{
|
||||
id: 'triggerInstructions',
|
||||
title: 'Setup Instructions',
|
||||
@@ -54,18 +66,6 @@ export const linearWebhookTrigger: TriggerConfig = {
|
||||
value: 'linear_webhook',
|
||||
},
|
||||
},
|
||||
{
|
||||
id: 'triggerSave',
|
||||
title: '',
|
||||
type: 'trigger-save',
|
||||
hideFromPreview: true,
|
||||
mode: 'trigger',
|
||||
triggerId: 'linear_webhook',
|
||||
condition: {
|
||||
field: 'selectedTriggerId',
|
||||
value: 'linear_webhook',
|
||||
},
|
||||
},
|
||||
],
|
||||
|
||||
outputs: {
|
||||
|
||||
@@ -72,6 +72,18 @@ export const microsoftTeamsChatSubscriptionTrigger: TriggerConfig = {
|
||||
value: 'microsoftteams_chat_subscription',
|
||||
},
|
||||
},
|
||||
{
|
||||
id: 'triggerSave',
|
||||
title: '',
|
||||
type: 'trigger-save',
|
||||
hideFromPreview: true,
|
||||
mode: 'trigger',
|
||||
triggerId: 'microsoftteams_chat_subscription',
|
||||
condition: {
|
||||
field: 'selectedTriggerId',
|
||||
value: 'microsoftteams_chat_subscription',
|
||||
},
|
||||
},
|
||||
{
|
||||
id: 'triggerInstructions',
|
||||
title: 'Setup Instructions',
|
||||
@@ -93,18 +105,6 @@ export const microsoftTeamsChatSubscriptionTrigger: TriggerConfig = {
|
||||
value: 'microsoftteams_chat_subscription',
|
||||
},
|
||||
},
|
||||
{
|
||||
id: 'triggerSave',
|
||||
title: '',
|
||||
type: 'trigger-save',
|
||||
hideFromPreview: true,
|
||||
mode: 'trigger',
|
||||
triggerId: 'microsoftteams_chat_subscription',
|
||||
condition: {
|
||||
field: 'selectedTriggerId',
|
||||
value: 'microsoftteams_chat_subscription',
|
||||
},
|
||||
},
|
||||
],
|
||||
|
||||
outputs: {
|
||||
|
||||
@@ -51,6 +51,18 @@ export const microsoftTeamsWebhookTrigger: TriggerConfig = {
|
||||
value: 'microsoftteams_webhook',
|
||||
},
|
||||
},
|
||||
{
|
||||
id: 'triggerSave',
|
||||
title: '',
|
||||
type: 'trigger-save',
|
||||
hideFromPreview: true,
|
||||
mode: 'trigger',
|
||||
triggerId: 'microsoftteams_webhook',
|
||||
condition: {
|
||||
field: 'selectedTriggerId',
|
||||
value: 'microsoftteams_webhook',
|
||||
},
|
||||
},
|
||||
{
|
||||
id: 'triggerInstructions',
|
||||
title: 'Setup Instructions',
|
||||
@@ -76,18 +88,6 @@ export const microsoftTeamsWebhookTrigger: TriggerConfig = {
|
||||
value: 'microsoftteams_webhook',
|
||||
},
|
||||
},
|
||||
{
|
||||
id: 'triggerSave',
|
||||
title: '',
|
||||
type: 'trigger-save',
|
||||
hideFromPreview: true,
|
||||
mode: 'trigger',
|
||||
triggerId: 'microsoftteams_webhook',
|
||||
condition: {
|
||||
field: 'selectedTriggerId',
|
||||
value: 'microsoftteams_webhook',
|
||||
},
|
||||
},
|
||||
],
|
||||
|
||||
outputs: {
|
||||
|
||||
@@ -93,6 +93,14 @@ export const outlookPollingTrigger: TriggerConfig = {
|
||||
required: false,
|
||||
mode: 'trigger',
|
||||
},
|
||||
{
|
||||
id: 'triggerSave',
|
||||
title: '',
|
||||
type: 'trigger-save',
|
||||
hideFromPreview: true,
|
||||
mode: 'trigger',
|
||||
triggerId: 'outlook_poller',
|
||||
},
|
||||
{
|
||||
id: 'triggerInstructions',
|
||||
title: 'Setup Instructions',
|
||||
@@ -110,14 +118,6 @@ export const outlookPollingTrigger: TriggerConfig = {
|
||||
.join(''),
|
||||
mode: 'trigger',
|
||||
},
|
||||
{
|
||||
id: 'triggerSave',
|
||||
title: '',
|
||||
type: 'trigger-save',
|
||||
hideFromPreview: true,
|
||||
mode: 'trigger',
|
||||
triggerId: 'outlook_poller',
|
||||
},
|
||||
],
|
||||
|
||||
outputs: {
|
||||
|
||||
@@ -19,6 +19,14 @@ export const rssPollingTrigger: TriggerConfig = {
|
||||
required: true,
|
||||
mode: 'trigger',
|
||||
},
|
||||
{
|
||||
id: 'triggerSave',
|
||||
title: '',
|
||||
type: 'trigger-save',
|
||||
hideFromPreview: true,
|
||||
mode: 'trigger',
|
||||
triggerId: 'rss_poller',
|
||||
},
|
||||
{
|
||||
id: 'triggerInstructions',
|
||||
title: 'Setup Instructions',
|
||||
@@ -36,14 +44,6 @@ export const rssPollingTrigger: TriggerConfig = {
|
||||
.join(''),
|
||||
mode: 'trigger',
|
||||
},
|
||||
{
|
||||
id: 'triggerSave',
|
||||
title: '',
|
||||
type: 'trigger-save',
|
||||
hideFromPreview: true,
|
||||
mode: 'trigger',
|
||||
triggerId: 'rss_poller',
|
||||
},
|
||||
],
|
||||
|
||||
outputs: {
|
||||
|
||||
@@ -30,6 +30,14 @@ export const slackWebhookTrigger: TriggerConfig = {
|
||||
required: true,
|
||||
mode: 'trigger',
|
||||
},
|
||||
{
|
||||
id: 'triggerSave',
|
||||
title: '',
|
||||
type: 'trigger-save',
|
||||
hideFromPreview: true,
|
||||
mode: 'trigger',
|
||||
triggerId: 'slack_webhook',
|
||||
},
|
||||
{
|
||||
id: 'triggerInstructions',
|
||||
title: 'Setup Instructions',
|
||||
@@ -41,7 +49,7 @@ export const slackWebhookTrigger: TriggerConfig = {
|
||||
'Go to "OAuth & Permissions" and add bot token scopes:<br><ul class="mt-1 ml-5 list-disc"><li><code>app_mentions:read</code> - For viewing messages that tag your bot with an @</li><li><code>chat:write</code> - To send messages to channels your bot is a part of</li></ul>',
|
||||
'Go to "Event Subscriptions":<br><ul class="mt-1 ml-5 list-disc"><li>Enable events</li><li>Under "Subscribe to Bot Events", add <code>app_mention</code> to listen to messages that mention your bot</li><li>Paste the Webhook URL above into the "Request URL" field</li></ul>',
|
||||
'Go to "Install App" in the left sidebar and install the app into your desired Slack workspace and channel.',
|
||||
'Save changes in both Slack and here.',
|
||||
'Save your changes in Slack. Your trigger will configure automatically once you complete the fields above.',
|
||||
]
|
||||
.map(
|
||||
(instruction, index) =>
|
||||
@@ -51,14 +59,6 @@ export const slackWebhookTrigger: TriggerConfig = {
|
||||
hideFromPreview: true,
|
||||
mode: 'trigger',
|
||||
},
|
||||
{
|
||||
id: 'triggerSave',
|
||||
title: '',
|
||||
type: 'trigger-save',
|
||||
hideFromPreview: true,
|
||||
mode: 'trigger',
|
||||
triggerId: 'slack_webhook',
|
||||
},
|
||||
],
|
||||
|
||||
outputs: {
|
||||
|
||||
@@ -165,6 +165,14 @@ export const stripeWebhookTrigger: TriggerConfig = {
|
||||
password: true,
|
||||
mode: 'trigger',
|
||||
},
|
||||
{
|
||||
id: 'triggerSave',
|
||||
title: '',
|
||||
type: 'trigger-save',
|
||||
hideFromPreview: true,
|
||||
mode: 'trigger',
|
||||
triggerId: 'stripe_webhook',
|
||||
},
|
||||
{
|
||||
id: 'triggerInstructions',
|
||||
title: 'Setup Instructions',
|
||||
@@ -178,7 +186,7 @@ export const stripeWebhookTrigger: TriggerConfig = {
|
||||
'Click "Create Destination" to save',
|
||||
'After creating the endpoint, click "Reveal" next to "Signing secret" and copy it',
|
||||
'Paste the signing secret into the <strong>Webhook Signing Secret</strong> field above',
|
||||
'Click "Save" to activate your webhook trigger',
|
||||
'Your webhook trigger will activate automatically once you complete the configuration above',
|
||||
]
|
||||
.map(
|
||||
(instruction, index) =>
|
||||
@@ -187,14 +195,6 @@ export const stripeWebhookTrigger: TriggerConfig = {
|
||||
.join(''),
|
||||
mode: 'trigger',
|
||||
},
|
||||
{
|
||||
id: 'triggerSave',
|
||||
title: '',
|
||||
type: 'trigger-save',
|
||||
hideFromPreview: true,
|
||||
mode: 'trigger',
|
||||
triggerId: 'stripe_webhook',
|
||||
},
|
||||
],
|
||||
|
||||
outputs: {
|
||||
|
||||
@@ -30,6 +30,14 @@ export const telegramWebhookTrigger: TriggerConfig = {
|
||||
required: true,
|
||||
mode: 'trigger',
|
||||
},
|
||||
{
|
||||
id: 'triggerSave',
|
||||
title: '',
|
||||
type: 'trigger-save',
|
||||
hideFromPreview: true,
|
||||
mode: 'trigger',
|
||||
triggerId: 'telegram_webhook',
|
||||
},
|
||||
{
|
||||
id: 'triggerInstructions',
|
||||
title: 'Setup Instructions',
|
||||
@@ -38,7 +46,7 @@ export const telegramWebhookTrigger: TriggerConfig = {
|
||||
defaultValue: [
|
||||
'Message "/newbot" to <a href="https://t.me/BotFather" target="_blank" rel="noopener noreferrer" class="text-muted-foreground underline transition-colors hover:text-muted-foreground/80">@BotFather</a> in Telegram to create a bot and copy its token.',
|
||||
'Enter your Bot Token above.',
|
||||
'Save settings and any message sent to your bot will trigger the workflow.',
|
||||
'Once configured, any message sent to your bot will trigger the workflow.',
|
||||
]
|
||||
.map(
|
||||
(instruction, index) =>
|
||||
@@ -47,14 +55,6 @@ export const telegramWebhookTrigger: TriggerConfig = {
|
||||
.join(''),
|
||||
mode: 'trigger',
|
||||
},
|
||||
{
|
||||
id: 'triggerSave',
|
||||
title: '',
|
||||
type: 'trigger-save',
|
||||
hideFromPreview: true,
|
||||
mode: 'trigger',
|
||||
triggerId: 'telegram_webhook',
|
||||
},
|
||||
],
|
||||
|
||||
outputs: {
|
||||
|
||||
@@ -49,6 +49,14 @@ export const twilioVoiceWebhookTrigger: TriggerConfig = {
|
||||
required: false,
|
||||
mode: 'trigger',
|
||||
},
|
||||
{
|
||||
id: 'triggerSave',
|
||||
title: '',
|
||||
type: 'trigger-save',
|
||||
hideFromPreview: true,
|
||||
mode: 'trigger',
|
||||
triggerId: 'twilio_voice_webhook',
|
||||
},
|
||||
{
|
||||
id: 'triggerInstructions',
|
||||
title: 'Setup Instructions',
|
||||
@@ -69,14 +77,6 @@ export const twilioVoiceWebhookTrigger: TriggerConfig = {
|
||||
.join('\n\n'),
|
||||
mode: 'trigger',
|
||||
},
|
||||
{
|
||||
id: 'triggerSave',
|
||||
title: '',
|
||||
type: 'trigger-save',
|
||||
hideFromPreview: true,
|
||||
mode: 'trigger',
|
||||
triggerId: 'twilio_voice_webhook',
|
||||
},
|
||||
],
|
||||
|
||||
outputs: {
|
||||
|
||||
@@ -61,6 +61,14 @@ export const typeformWebhookTrigger: TriggerConfig = {
|
||||
defaultValue: false,
|
||||
mode: 'trigger',
|
||||
},
|
||||
{
|
||||
id: 'triggerSave',
|
||||
title: '',
|
||||
type: 'trigger-save',
|
||||
hideFromPreview: true,
|
||||
mode: 'trigger',
|
||||
triggerId: 'typeform_webhook',
|
||||
},
|
||||
{
|
||||
id: 'triggerInstructions',
|
||||
title: 'Setup Instructions',
|
||||
@@ -71,7 +79,7 @@ export const typeformWebhookTrigger: TriggerConfig = {
|
||||
'Find your Form ID in the URL when editing your form (e.g., <code>https://admin.typeform.com/form/ABC123/create</code> → Form ID is <code>ABC123</code>)',
|
||||
'Fill in the form above with your Form ID and Personal Access Token',
|
||||
'Optionally add a Webhook Secret for enhanced security - Sim will verify all incoming webhooks match this secret',
|
||||
'Click "Save" below - Sim will automatically register the webhook with Typeform',
|
||||
'Sim will automatically register the webhook with Typeform when you complete the configuration above',
|
||||
'<strong>Note:</strong> Requires a Typeform PRO or PRO+ account to use webhooks',
|
||||
]
|
||||
.map(
|
||||
@@ -81,14 +89,6 @@ export const typeformWebhookTrigger: TriggerConfig = {
|
||||
.join(''),
|
||||
mode: 'trigger',
|
||||
},
|
||||
{
|
||||
id: 'triggerSave',
|
||||
title: '',
|
||||
type: 'trigger-save',
|
||||
hideFromPreview: true,
|
||||
mode: 'trigger',
|
||||
triggerId: 'typeform_webhook',
|
||||
},
|
||||
],
|
||||
|
||||
outputs: {
|
||||
|
||||
@@ -53,6 +53,18 @@ export const webflowCollectionItemChangedTrigger: TriggerConfig = {
|
||||
value: 'webflow_collection_item_changed',
|
||||
},
|
||||
},
|
||||
{
|
||||
id: 'triggerSave',
|
||||
title: '',
|
||||
type: 'trigger-save',
|
||||
hideFromPreview: true,
|
||||
mode: 'trigger',
|
||||
triggerId: 'webflow_collection_item_changed',
|
||||
condition: {
|
||||
field: 'selectedTriggerId',
|
||||
value: 'webflow_collection_item_changed',
|
||||
},
|
||||
},
|
||||
{
|
||||
id: 'triggerInstructions',
|
||||
title: 'Setup Instructions',
|
||||
@@ -77,18 +89,6 @@ export const webflowCollectionItemChangedTrigger: TriggerConfig = {
|
||||
value: 'webflow_collection_item_changed',
|
||||
},
|
||||
},
|
||||
{
|
||||
id: 'triggerSave',
|
||||
title: '',
|
||||
type: 'trigger-save',
|
||||
hideFromPreview: true,
|
||||
mode: 'trigger',
|
||||
triggerId: 'webflow_collection_item_changed',
|
||||
condition: {
|
||||
field: 'selectedTriggerId',
|
||||
value: 'webflow_collection_item_changed',
|
||||
},
|
||||
},
|
||||
],
|
||||
|
||||
outputs: {
|
||||
|
||||
@@ -66,6 +66,18 @@ export const webflowCollectionItemCreatedTrigger: TriggerConfig = {
|
||||
value: 'webflow_collection_item_created',
|
||||
},
|
||||
},
|
||||
{
|
||||
id: 'triggerSave',
|
||||
title: '',
|
||||
type: 'trigger-save',
|
||||
hideFromPreview: true,
|
||||
mode: 'trigger',
|
||||
triggerId: 'webflow_collection_item_created',
|
||||
condition: {
|
||||
field: 'selectedTriggerId',
|
||||
value: 'webflow_collection_item_created',
|
||||
},
|
||||
},
|
||||
{
|
||||
id: 'triggerInstructions',
|
||||
title: 'Setup Instructions',
|
||||
@@ -90,18 +102,6 @@ export const webflowCollectionItemCreatedTrigger: TriggerConfig = {
|
||||
value: 'webflow_collection_item_created',
|
||||
},
|
||||
},
|
||||
{
|
||||
id: 'triggerSave',
|
||||
title: '',
|
||||
type: 'trigger-save',
|
||||
hideFromPreview: true,
|
||||
mode: 'trigger',
|
||||
triggerId: 'webflow_collection_item_created',
|
||||
condition: {
|
||||
field: 'selectedTriggerId',
|
||||
value: 'webflow_collection_item_created',
|
||||
},
|
||||
},
|
||||
],
|
||||
|
||||
outputs: {
|
||||
|
||||
@@ -53,6 +53,18 @@ export const webflowCollectionItemDeletedTrigger: TriggerConfig = {
|
||||
value: 'webflow_collection_item_deleted',
|
||||
},
|
||||
},
|
||||
{
|
||||
id: 'triggerSave',
|
||||
title: '',
|
||||
type: 'trigger-save',
|
||||
hideFromPreview: true,
|
||||
mode: 'trigger',
|
||||
triggerId: 'webflow_collection_item_deleted',
|
||||
condition: {
|
||||
field: 'selectedTriggerId',
|
||||
value: 'webflow_collection_item_deleted',
|
||||
},
|
||||
},
|
||||
{
|
||||
id: 'triggerInstructions',
|
||||
title: 'Setup Instructions',
|
||||
@@ -78,18 +90,6 @@ export const webflowCollectionItemDeletedTrigger: TriggerConfig = {
|
||||
value: 'webflow_collection_item_deleted',
|
||||
},
|
||||
},
|
||||
{
|
||||
id: 'triggerSave',
|
||||
title: '',
|
||||
type: 'trigger-save',
|
||||
hideFromPreview: true,
|
||||
mode: 'trigger',
|
||||
triggerId: 'webflow_collection_item_deleted',
|
||||
condition: {
|
||||
field: 'selectedTriggerId',
|
||||
value: 'webflow_collection_item_deleted',
|
||||
},
|
||||
},
|
||||
],
|
||||
|
||||
outputs: {
|
||||
|
||||
@@ -40,6 +40,14 @@ export const webflowFormSubmissionTrigger: TriggerConfig = {
|
||||
required: false,
|
||||
mode: 'trigger',
|
||||
},
|
||||
{
|
||||
id: 'triggerSave',
|
||||
title: '',
|
||||
type: 'trigger-save',
|
||||
hideFromPreview: true,
|
||||
mode: 'trigger',
|
||||
triggerId: 'webflow_form_submission',
|
||||
},
|
||||
{
|
||||
id: 'triggerInstructions',
|
||||
title: 'Setup Instructions',
|
||||
@@ -61,14 +69,6 @@ export const webflowFormSubmissionTrigger: TriggerConfig = {
|
||||
.join(''),
|
||||
mode: 'trigger',
|
||||
},
|
||||
{
|
||||
id: 'triggerSave',
|
||||
title: '',
|
||||
type: 'trigger-save',
|
||||
hideFromPreview: true,
|
||||
mode: 'trigger',
|
||||
triggerId: 'webflow_form_submission',
|
||||
},
|
||||
],
|
||||
|
||||
outputs: {
|
||||
|
||||
@@ -31,6 +31,14 @@ export const whatsappWebhookTrigger: TriggerConfig = {
|
||||
required: true,
|
||||
mode: 'trigger',
|
||||
},
|
||||
{
|
||||
id: 'triggerSave',
|
||||
title: '',
|
||||
type: 'trigger-save',
|
||||
hideFromPreview: true,
|
||||
mode: 'trigger',
|
||||
triggerId: 'whatsapp_webhook',
|
||||
},
|
||||
{
|
||||
id: 'triggerInstructions',
|
||||
title: 'Setup Instructions',
|
||||
@@ -53,14 +61,6 @@ export const whatsappWebhookTrigger: TriggerConfig = {
|
||||
.join(''),
|
||||
mode: 'trigger',
|
||||
},
|
||||
{
|
||||
id: 'triggerSave',
|
||||
title: '',
|
||||
type: 'trigger-save',
|
||||
hideFromPreview: true,
|
||||
mode: 'trigger',
|
||||
triggerId: 'whatsapp_webhook',
|
||||
},
|
||||
],
|
||||
|
||||
outputs: {
|
||||
|
||||
Reference in New Issue
Block a user