File size: 3,653 Bytes
c09f67c | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 | import { format, parse, parseISO } from "date-fns";
/**
* Maps chart type identifiers to natural language names
*/
const chartTypeMap: Record<string, string> = {
"monthly-revenue": "revenue",
revenue: "revenue",
profit: "profit",
"burn-rate": "burn rate",
expenses: "expenses",
"revenue-forecast": "revenue forecast",
runway: "runway",
"category-expenses": "category expenses",
"stacked-bar": "expenses",
"revenue-trend": "revenue trends",
"cash-flow": "cash flow",
"growth-rate": "growth rate",
"business-health-score": "business health score",
"invoice-payment": "invoice payments",
"tax-trend": "tax trends",
"stress-test": "cash flow stress test",
};
/**
* Gets a natural language name for a chart type
*/
export function getChartTypeName(chartId: string): string {
return chartTypeMap[chartId] || chartId;
}
/**
* Parses a date string (could be ISO string, month name, etc.) and returns a Date object
*/
function parseDate(dateStr: string): Date | null {
// Try ISO string first (use parseISO for correct timezone handling)
if (dateStr.includes("T") || dateStr.includes("-")) {
const parsed = parseISO(dateStr);
if (!Number.isNaN(parsed.getTime())) {
return parsed;
}
}
// Try month abbreviations (Jan, Feb, etc.)
const monthMap: Record<string, number> = {
Jan: 0,
Feb: 1,
Mar: 2,
Apr: 3,
May: 4,
Jun: 5,
Jul: 6,
Aug: 7,
Sep: 8,
Oct: 9,
Nov: 10,
Dec: 11,
};
if (monthMap[dateStr] !== undefined) {
// Use current year for month-only dates
const now = new Date();
return new Date(now.getFullYear(), monthMap[dateStr], 1);
}
// Try parsing with date-fns common formats
const formats = ["MMM", "MMMM", "yyyy-MM-dd", "MM/dd/yyyy"];
for (const fmt of formats) {
try {
const parsed = parse(dateStr, fmt, new Date());
if (!Number.isNaN(parsed.getTime())) {
return parsed;
}
} catch {
// Continue to next format
}
}
return null;
}
/**
* Formats a date range for natural language display
*/
function formatDateRange(startDate: Date, endDate: Date): string {
const now = new Date();
const currentYear = now.getFullYear();
// Check if both dates are in the same year
const startYear = startDate.getFullYear();
const endYear = endDate.getFullYear();
// If dates are months only (day is 1), format as month names
if (
startDate.getDate() === 1 &&
endDate.getDate() === 1 &&
startYear === endYear &&
startYear === currentYear
) {
const startMonth = format(startDate, "MMMM");
const endMonth = format(endDate, "MMMM");
if (startMonth === endMonth) {
return startMonth;
}
return `${startMonth} to ${endMonth}`;
}
// Format as full dates if different years or specific dates
const startFormatted = format(startDate, "MMMM d");
const endFormatted = format(endDate, "MMMM d");
if (startFormatted === endFormatted) {
return startFormatted;
}
return `${startFormatted} to ${endFormatted}`;
}
/**
* Generates a natural language message for chart period selection
*/
export function generateChartSelectionMessage(
startDate: string,
endDate: string,
chartType: string,
): string {
const chartName = getChartTypeName(chartType);
const start = parseDate(startDate);
const end = parseDate(endDate);
if (!start || !end) {
// Fallback to simple format if parsing fails
return `Show me a breakdown of ${chartName} from ${startDate} to ${endDate}`;
}
const dateRange = formatDateRange(start, end);
return `Show me a breakdown of ${chartName} from ${dateRange}`;
}
|