// src/formatters/StoryFormatter.ts
import { JiraStory } from "../types/JiraTypes";
import { JiraConstants } from "../constants/JiraConstants";
export class StoryFormatter {
public formatStoryDetails(story: JiraStory): string {
const sections = [
this.formatHeader(story),
this.formatMetadata(story),
this.formatDescription(story),
this.formatComments(story),
];
return sections
.filter((section) => section)
.join("\n\n")
.trim();
}
public formatStoryList(stories: JiraStory[]): string {
return stories.map((story) => this.formatStoryListItem(story)).join("\n");
}
public formatStoryListItem(story: JiraStory): string {
const assignee = story.assignee || JiraConstants.UNASSIGNED_LABEL;
return `${story.key}: ${story.title} [${story.status}] - ${assignee}`;
}
public formatStorySummary(story: JiraStory): string {
const type = story.type || JiraConstants.ISSUE_TYPE_STORY;
const priority = story.priority || JiraConstants.NOT_SET_LABEL;
const assignee = story.assignee || JiraConstants.UNASSIGNED_LABEL;
const descriptionPreview = this.truncateDescription(story.description);
return `### ${story.key}: ${story.title}
**Status:** ${story.status} | **Type:** ${type} | **Priority:** ${priority}
**Assignee:** ${assignee}
${descriptionPreview}`.trim();
}
private formatHeader(story: JiraStory): string {
return `# ${story.key}: ${story.title}`;
}
private formatMetadata(story: JiraStory): string {
const lines = [
`**Status:** ${story.status}`,
`**Type:** ${story.type || JiraConstants.ISSUE_TYPE_STORY}`,
`**Priority:** ${story.priority || JiraConstants.NOT_SET_LABEL}`,
`**Assignee:** ${story.assignee || JiraConstants.UNASSIGNED_LABEL}`,
`**Reporter:** ${story.reporter || JiraConstants.UNKNOWN_LABEL}`,
`**Created:** ${this.formatDate(story.created)}`,
`**Updated:** ${this.formatDate(story.updated)}`,
];
if (story.labels && story.labels.length > 0) {
lines.push(`**Labels:** ${story.labels.join(", ")}`);
}
return lines.join("\n");
}
private formatDescription(story: JiraStory): string {
const description = story.description || "No description provided";
return `## Description\n${description}`;
}
private formatComments(story: JiraStory): string {
if (!story.comments || story.comments.length === 0) {
return "";
}
const header = `## Comments (${story.comments.length})`;
const commentsList = story.comments
.map((comment) => this.formatComment(comment))
.join("\n");
return `${header}\n${commentsList}`;
}
private formatComment(comment: any): string {
return `**${comment.author}** (${this.formatDate(comment.created)}):\n${
comment.body
}`;
}
private formatDate(isoDate: string): string {
return new Date(isoDate).toLocaleDateString();
}
private truncateDescription(
description: string,
maxLength: number = 200
): string {
if (!description) {
return "";
}
if (description.length <= maxLength) {
return `**Description:** ${description}`;
}
return `**Description:** ${description.substring(0, maxLength)}...`;
}
}