shortcutHelpers.tsā¢6.66 kB
import type { Workflow, CreateStoryParams } from './shortcut-types'
// Common labels used by the team
export const COMMON_LABELS = {
BUG_INVESTIGATE: 'š§ Investigate',
BUG_BASH: 'š BASH',
INTERCOM: 'Intercom',
AUDIENCE: 'Audience',
TAG: 'Tag',
KIOSK: 'Kiosk',
} as const
// Custom field IDs and values
export const CUSTOM_FIELDS = {
PRIORITY: {
FIELD_ID: '6260465b-2009-456c-b2fe-ce32bb284231',
VALUES: {
HIGHEST: '6260465b-9ae6-4667-88d6-51f634f75d6b',
MEDIUM: '6260465b-552c-4138-83ec-a52cb9f6f4f5',
LOW: '6260465b-a812-4a6d-8e71-4dce16aed4f5',
},
},
} as const
// Story template IDs
export const STORY_TEMPLATES = {
BUG_REPORT: '5fda555d-02f3-4741-b9a3-8b263c0be423',
} as const
// Common epic IDs
export const COMMON_EPICS = {
BUGS: 27912,
} as const
// Workflow state IDs (from Development workflow)
export const WORKFLOW_STATES = {
DEVELOPMENT: {
ICEBOX: 500000008,
ON_DECK: 500000249,
BACKLOG: 500000007,
IN_PROGRESS: 500000006,
READY_FOR_REVIEW: 500000010,
MERGED: 500000038,
READY_FOR_QA: 500000040,
READY_FOR_DEPLOY: 500000009,
DEPLOYED: 500003111,
COMPLETED: 500000011,
},
} as const
/**
* Get default workflow state for a project
*/
export function getDefaultWorkflowState(workflows: Workflow[], projectId?: number): number | undefined {
// Find the workflow for the project
const workflow = workflows.find(w =>
!projectId || w.project_ids.includes(projectId)
)
if (!workflow) return undefined
// Return the default state or first unstarted state
return workflow.default_state_id || workflow.states.find(s => s.type === 'unstarted')?.id
}
/**
* Generate a bug story title with emoji prefix if needed
*/
export function formatBugTitle(title: string): string {
// Check if title already has bug emoji
if (title.startsWith('š')) return title
// Add bug emoji prefix
return `š ${title.trim()}`
}
/**
* Generate default labels based on story type and content
*/
export function getDefaultLabels(storyType: string, title: string, description?: string): Array<{ name: string }> {
const labels: Array<{ name: string }> = []
if (storyType === 'bug') {
// Add investigate label for bugs by default
labels.push({ name: COMMON_LABELS.BUG_INVESTIGATE })
// Check content for specific keywords
const content = `${title} ${description || ''}`.toLowerCase()
if (content.includes('kiosk')) {
labels.push({ name: COMMON_LABELS.KIOSK })
}
if (content.includes('audience')) {
labels.push({ name: COMMON_LABELS.AUDIENCE })
}
if (content.includes('tag')) {
labels.push({ name: COMMON_LABELS.TAG })
}
if (content.includes('intercom') || content.includes('customer') || content.includes('partner')) {
labels.push({ name: COMMON_LABELS.INTERCOM })
}
}
return labels
}
/**
* Generate smart defaults for story creation
*/
export function getSmartDefaults(
params: Partial<CreateStoryParams>,
workflows: Workflow[]
): Partial<CreateStoryParams> {
const defaults: Partial<CreateStoryParams> = {}
// Set default workflow state if not provided
if (!params.workflow_state_id && params.project_id) {
const defaultState = getDefaultWorkflowState(workflows, params.project_id)
if (defaultState) {
defaults.workflow_state_id = defaultState
}
}
// Format bug titles
if (params.story_type === 'bug' && params.name) {
defaults.name = formatBugTitle(params.name)
}
// Set default labels based on content
if (!params.labels && params.story_type) {
const defaultLabels = getDefaultLabels(
params.story_type,
params.name || '',
params.description
)
if (defaultLabels.length > 0) {
defaults.labels = defaultLabels
}
}
// Set default epic for bugs
if (params.story_type === 'bug' && !params.epic_id) {
defaults.epic_id = COMMON_EPICS.BUGS
}
// Set bug template if it's a bug without a template
if (params.story_type === 'bug' && !params.story_template_id) {
defaults.story_template_id = STORY_TEMPLATES.BUG_REPORT
}
// Set default priority for bugs
if (params.story_type === 'bug' && !params.custom_fields) {
defaults.custom_fields = [{
field_id: CUSTOM_FIELDS.PRIORITY.FIELD_ID,
value_id: CUSTOM_FIELDS.PRIORITY.VALUES.MEDIUM,
}]
}
// Set default deadline for bugs (next business day)
if (params.story_type === 'bug' && !params.deadline) {
const tomorrow = new Date()
tomorrow.setDate(tomorrow.getDate() + 1)
// Skip weekends
if (tomorrow.getDay() === 0) tomorrow.setDate(tomorrow.getDate() + 1) // Sunday -> Monday
if (tomorrow.getDay() === 6) tomorrow.setDate(tomorrow.getDate() + 2) // Saturday -> Monday
defaults.deadline = tomorrow.toISOString().split('T')[0] + 'T07:00:00Z'
}
return defaults
}
/**
* Generate bug report template content
*/
export function generateBugReportTemplate(
title: string,
reporterDescription?: string,
dashboardLinks?: string[],
orgIds?: string[],
loomUrl?: string
): string {
let template = `# REPORTER:
### Please give a detailed description of the issue, and supply all user/membership/class IDs where necessary:\t
`
if (dashboardLinks && dashboardLinks.length > 0) {
template += `**Dashboard Link(s):** ${dashboardLinks.join(', ')}\n`
}
if (orgIds && orgIds.length > 0) {
template += `**Org ID(s):** ${orgIds.join(', ')}\n`
}
template += `\t\t\t\t\t
**Current behavior:** What is currently happening? \t
${reporterDescription || '[Describe the issue here]'}
**Expected behavior:** What is expected?
[Describe expected behavior]
**Steps to reproduce:** How can the team reproduce this issue? Share exact steps and device to use
1. [Step 1]
2. [Step 2]
3. [Step 3]
`
if (loomUrl) {
template += `**Loom:** ${loomUrl}\n`
}
template += `
.
.
.
# ON CALL DEV:
**Code Area:**
**Complexity:** High / Medium / Low
**Fix LOE:** High = Multiple Days / Medium = 1 Day / Low = Less than 1 Day
**Investigation Result:** What did you learn?
**Implementation Plan:** If you know how to fix it, what should we do?`
return template
}