Fix Monday.com date parsing and normalize dropdown labels
- Rewrite parseToISO to handle many OCR date formats: "july 20, 02", "20/May/1999", "08/JUN/26", "Oct / 02 / 2004", "Feb 19, 2016", etc. - Return null instead of raw string when date parsing fails, preventing Monday.com ColumnValueException for unparseable dates - Normalize long nextStep text from pre-update OCR runs (e.g. "Expressing my faith in Jesus..." -> "Baptism") before sending to Monday.com dropdowns Made-with: Cursor
This commit is contained in:
parent
daec81884c
commit
ec51c049c3
1 changed files with 91 additions and 9 deletions
|
|
@ -155,6 +155,20 @@ function toCleanString(val: unknown): string | null {
|
|||
return str;
|
||||
}
|
||||
|
||||
const DROPDOWN_NORMALIZATIONS: [RegExp, string][] = [
|
||||
[/expressing my faith in jesus/i, "Baptism"],
|
||||
[/i want to be baptized/i, "Baptism"],
|
||||
[/learning more about becoming a partner/i, "Next Steps"],
|
||||
[/attend next steps/i, "Next Steps"],
|
||||
];
|
||||
|
||||
function normalizeDropdownLabel(label: string): string {
|
||||
for (const [pattern, replacement] of DROPDOWN_NORMALIZATIONS) {
|
||||
if (pattern.test(label)) return replacement;
|
||||
}
|
||||
return label;
|
||||
}
|
||||
|
||||
function formatForColumnType(colType: string, val: unknown): unknown | null {
|
||||
switch (colType) {
|
||||
case "boolean":
|
||||
|
|
@ -172,15 +186,20 @@ function formatForColumnType(colType: string, val: unknown): unknown | null {
|
|||
case "dropdown": {
|
||||
const str = toCleanString(val);
|
||||
if (!str) return null;
|
||||
const labels = str.split(",").map((s) => s.trim()).filter(Boolean);
|
||||
const labels = str.split(",").map((s) => normalizeDropdownLabel(s.trim())).filter(Boolean);
|
||||
return labels.length > 0 ? { labels } : null;
|
||||
}
|
||||
|
||||
case "date": {
|
||||
const str = toCleanString(val);
|
||||
if (!str) return null;
|
||||
if (typeof val === "object" && !Array.isArray(val) && (val as Record<string, unknown>).date) {
|
||||
const inner = String((val as Record<string, unknown>).date);
|
||||
const iso = parseToISO(inner);
|
||||
return iso ? { date: iso } : null;
|
||||
}
|
||||
const iso = parseToISO(str);
|
||||
return iso ? { date: iso } : str;
|
||||
return iso ? { date: iso } : null;
|
||||
}
|
||||
|
||||
case "numeric":
|
||||
|
|
@ -212,14 +231,77 @@ function formatForColumnType(colType: string, val: unknown): unknown | null {
|
|||
}
|
||||
}
|
||||
|
||||
const MONTH_MAP: Record<string, string> = {
|
||||
jan: "01", january: "01", feb: "02", february: "02",
|
||||
mar: "03", march: "03", apr: "04", april: "04",
|
||||
may: "05", jun: "06", june: "06", jul: "07", july: "07",
|
||||
aug: "08", august: "08", sep: "09", september: "09",
|
||||
oct: "10", october: "10", nov: "11", november: "11",
|
||||
dec: "12", december: "12",
|
||||
};
|
||||
|
||||
function normalizeYear(y: string): string {
|
||||
if (y.length === 4) return y;
|
||||
const n = parseInt(y);
|
||||
return n > 50 ? `19${y.padStart(2, "0")}` : `20${y.padStart(2, "0")}`;
|
||||
}
|
||||
|
||||
function parseToISO(dateStr: string): string | null {
|
||||
const parts = dateStr.match(/^(\d{1,2})[/\-.](\d{1,2})[/\-.](\d{2,4})$/);
|
||||
if (!parts) return null;
|
||||
let [, m, d, y] = parts;
|
||||
if (y.length === 2) y = parseInt(y) > 50 ? `19${y}` : `20${y}`;
|
||||
const month = m.padStart(2, "0");
|
||||
const day = d.padStart(2, "0");
|
||||
return `${y}-${month}-${day}`;
|
||||
const s = dateStr.replace(/\s+/g, " ").trim();
|
||||
|
||||
// MM/DD/YYYY or DD/MM/YYYY (all-numeric)
|
||||
const numParts = s.match(/^(\d{1,2})\s*[/\-.]\s*(\d{1,2})\s*[/\-.]\s*(\d{2,4})$/);
|
||||
if (numParts) {
|
||||
let [, a, b, y] = numParts;
|
||||
y = normalizeYear(y);
|
||||
const aNum = parseInt(a), bNum = parseInt(b);
|
||||
let month: string, day: string;
|
||||
if (aNum > 12 && bNum <= 12) {
|
||||
day = a; month = b;
|
||||
} else {
|
||||
month = a; day = b;
|
||||
}
|
||||
return `${y}-${month.padStart(2, "0")}-${day.padStart(2, "0")}`;
|
||||
}
|
||||
|
||||
// DD/Mon/YYYY e.g. "20/May/1999", "08/JUN/26"
|
||||
const dmyNamed = s.match(/^(\d{1,2})\s*[/\-.]\s*([A-Za-z]+)\s*[/\-.]\s*(\d{2,4})$/);
|
||||
if (dmyNamed) {
|
||||
const [, d, mName, y] = dmyNamed;
|
||||
const month = MONTH_MAP[mName.toLowerCase()];
|
||||
if (month) return `${normalizeYear(y)}-${month}-${d.padStart(2, "0")}`;
|
||||
}
|
||||
|
||||
// "Month DD, YY(YY)" e.g. "july 20, 02", "Feb 19, 2016", "October 5, 1990"
|
||||
const mdyNamed = s.match(/^([A-Za-z]+)\s+(\d{1,2}),?\s*(\d{2,4})$/);
|
||||
if (mdyNamed) {
|
||||
const [, mName, d, y] = mdyNamed;
|
||||
const month = MONTH_MAP[mName.toLowerCase()];
|
||||
if (month) return `${normalizeYear(y)}-${month}-${d.padStart(2, "0")}`;
|
||||
}
|
||||
|
||||
// "Mon / DD / YYYY" with spaces e.g. "Oct / 02 / 2004"
|
||||
const spacedNamed = s.match(/^([A-Za-z]+)\s*[/\-.]\s*(\d{1,2})\s*[/\-.]\s*(\d{2,4})$/);
|
||||
if (spacedNamed) {
|
||||
const [, mName, d, y] = spacedNamed;
|
||||
const month = MONTH_MAP[mName.toLowerCase()];
|
||||
if (month) return `${normalizeYear(y)}-${month}-${d.padStart(2, "0")}`;
|
||||
}
|
||||
|
||||
// YYYY-MM-DD already ISO
|
||||
const iso = s.match(/^(\d{4})-(\d{2})-(\d{2})$/);
|
||||
if (iso) return s;
|
||||
|
||||
// Last resort: try native Date parsing
|
||||
const d = new Date(s);
|
||||
if (!isNaN(d.getTime()) && d.getFullYear() > 1900) {
|
||||
const yyyy = String(d.getFullYear());
|
||||
const mm = String(d.getMonth() + 1).padStart(2, "0");
|
||||
const dd = String(d.getDate()).padStart(2, "0");
|
||||
return `${yyyy}-${mm}-${dd}`;
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
export function mapCardToColumnValues(
|
||||
|
|
|
|||
Loading…
Reference in a new issue