Remove moved-by and from-status, rename to-status to Status, and show Created/Updated in America/New_York with EDT/EST. Co-authored-by: Cursor <cursoragent@cursor.com>
172 lines
5.5 KiB
JavaScript
172 lines
5.5 KiB
JavaScript
import fs from 'fs';
|
|
import { formatJiraRichText } from './jiraRichText.js';
|
|
|
|
const SKIP_FIELD_IDS = new Set(['attachment']);
|
|
const EASTERN_TIME_ZONE = 'America/New_York';
|
|
|
|
export function formatEasternDateTime(value) {
|
|
const date = new Date(value);
|
|
if (Number.isNaN(date.getTime())) {
|
|
return String(value);
|
|
}
|
|
|
|
const formatted = new Intl.DateTimeFormat('en-US', {
|
|
timeZone: EASTERN_TIME_ZONE,
|
|
month: 'numeric',
|
|
day: 'numeric',
|
|
year: 'numeric',
|
|
hour: 'numeric',
|
|
minute: '2-digit',
|
|
second: '2-digit',
|
|
hour12: true,
|
|
}).format(date);
|
|
|
|
const timeZoneName = new Intl.DateTimeFormat('en-US', {
|
|
timeZone: EASTERN_TIME_ZONE,
|
|
timeZoneName: 'short',
|
|
}).formatToParts(date).find(part => part.type === 'timeZoneName')?.value || 'ET';
|
|
|
|
return `${formatted} ${timeZoneName}`;
|
|
}
|
|
|
|
export function formatEasternDate(value) {
|
|
const date = new Date(value);
|
|
if (Number.isNaN(date.getTime())) {
|
|
return String(value);
|
|
}
|
|
|
|
return new Intl.DateTimeFormat('en-US', {
|
|
timeZone: EASTERN_TIME_ZONE,
|
|
month: 'numeric',
|
|
day: 'numeric',
|
|
year: 'numeric',
|
|
}).format(date);
|
|
}
|
|
|
|
export function loadRequestTypeFields() {
|
|
const path = './config/requestTypeFields.json';
|
|
if (!fs.existsSync(path)) {
|
|
return { common: { displayFields: [] }, groups: {}, requestTypes: {} };
|
|
}
|
|
return JSON.parse(fs.readFileSync(path, 'utf8'));
|
|
}
|
|
|
|
function resolveFieldEntry(entry, groups) {
|
|
if (typeof entry === 'string' && groups[entry]) {
|
|
return groups[entry].displayFields || [];
|
|
}
|
|
return [entry];
|
|
}
|
|
|
|
export function getDisplayFieldsForRequestType(requestTypeId, fieldMap = loadRequestTypeFields()) {
|
|
const key = String(requestTypeId);
|
|
const typeConfig = fieldMap.requestTypes?.[key];
|
|
if (!typeConfig) {
|
|
return fieldMap.common?.displayFields || [];
|
|
}
|
|
|
|
const groups = fieldMap.groups || {};
|
|
const resolved = [];
|
|
|
|
if (typeConfig.includeCommon !== false) {
|
|
resolved.push(...(fieldMap.common?.displayFields || []));
|
|
}
|
|
|
|
for (const groupRef of typeConfig.groups || []) {
|
|
const groupFields = groups[groupRef]?.displayFields || [];
|
|
resolved.push(...groupFields);
|
|
}
|
|
|
|
for (const field of typeConfig.displayFields || []) {
|
|
resolved.push(...resolveFieldEntry(field, groups));
|
|
}
|
|
|
|
const seen = new Set();
|
|
return resolved.filter(field => {
|
|
const dedupeKey = field.path || field.fieldId;
|
|
if (!dedupeKey || seen.has(dedupeKey) || SKIP_FIELD_IDS.has(field.fieldId)) {
|
|
return false;
|
|
}
|
|
seen.add(dedupeKey);
|
|
return true;
|
|
});
|
|
}
|
|
|
|
export function formatFieldValue(value, format) {
|
|
if (value == null || value === '') {
|
|
return null;
|
|
}
|
|
|
|
switch (format) {
|
|
case 'option':
|
|
return value.value ?? value.name ?? String(value);
|
|
case 'user':
|
|
return value.displayName ?? value.emailAddress ?? String(value);
|
|
case 'users':
|
|
if (!Array.isArray(value)) {
|
|
return formatFieldValue(value, 'user');
|
|
}
|
|
return value.map(item => formatFieldValue(item, 'user')).filter(Boolean).join(', ');
|
|
case 'datetime':
|
|
return formatEasternDateTime(value);
|
|
case 'date':
|
|
return formatEasternDate(value);
|
|
case 'cmdb':
|
|
if (Array.isArray(value)) {
|
|
return value.map(item => item.label || item.name || item.objectKey || item.value).filter(Boolean).join(', ');
|
|
}
|
|
return value.label || value.name || value.objectKey || String(value);
|
|
case 'multiselect':
|
|
case 'multicheckboxes':
|
|
if (!Array.isArray(value)) {
|
|
return String(value);
|
|
}
|
|
return value.map(item => item.value || item.label || String(item)).join(', ');
|
|
case 'cascading':
|
|
if (value.child) {
|
|
return `${value.value} → ${value.child.value}`;
|
|
}
|
|
return value.value ?? String(value);
|
|
case 'textarea':
|
|
case 'text':
|
|
default:
|
|
if (typeof value === 'object') {
|
|
const richText = formatJiraRichText(value);
|
|
return richText || value.displayName || value.value || value.name || null;
|
|
}
|
|
return formatJiraRichText(value) || null;
|
|
}
|
|
}
|
|
|
|
export function getFieldValueFromTicket(jiraTicket, fieldDef) {
|
|
if (fieldDef.path) {
|
|
return fieldDef.path.split('.').reduce((current, part) => current?.[part], jiraTicket);
|
|
}
|
|
|
|
const fieldId = fieldDef.fieldId;
|
|
if (!fieldId) {
|
|
return null;
|
|
}
|
|
|
|
if (fieldId === 'summary' || fieldId === 'description' || fieldId === 'priority' || fieldId === 'created' || fieldId === 'updated') {
|
|
const fields = jiraTicket.issue?.fields || {};
|
|
return fields[fieldId];
|
|
}
|
|
|
|
return jiraTicket.issue?.fields?.[fieldId] ?? null;
|
|
}
|
|
|
|
export function buildDetailLinesForRequestType(jiraTicket, requestTypeId, fieldMap = loadRequestTypeFields()) {
|
|
const fields = getDisplayFieldsForRequestType(requestTypeId, fieldMap);
|
|
const lines = [];
|
|
|
|
for (const fieldDef of fields) {
|
|
const rawValue = getFieldValueFromTicket(jiraTicket, fieldDef);
|
|
const formatted = formatFieldValue(rawValue, fieldDef.format);
|
|
if (formatted) {
|
|
lines.push({ label: fieldDef.label, value: formatted, format: fieldDef.format });
|
|
}
|
|
}
|
|
|
|
return lines;
|
|
}
|