True clean start with src folder

This commit is contained in:
2026-03-10 19:05:37 -05:00
parent 806ebad1c3
commit 5a8b20a827
25 changed files with 3305 additions and 1 deletions
+114
View File
@@ -0,0 +1,114 @@
export interface ExtractedItem {
id: string;
description: string;
quantity: string;
}
export function parseCSV(text: string): string[][] {
const rows: string[][] = [];
let currentRow: string[] = [];
let currentCell = '';
let inQuotes = false;
for (let i = 0; i < text.length; i++) {
const char = text[i];
const nextChar = text[i + 1];
if (char === '"') {
if (inQuotes && nextChar === '"') {
currentCell += '"';
i++;
} else if (!inQuotes) {
inQuotes = true;
} else {
const isClosing = nextChar === ',' || nextChar === '\r' || nextChar === '\n' || nextChar === undefined;
if (isClosing) {
inQuotes = false;
} else {
currentCell += '"';
}
}
} else if (char === ',' && !inQuotes) {
currentRow.push(currentCell);
currentCell = '';
} else if ((char === '\r' || char === '\n') && !inQuotes) {
if (currentCell || currentRow.length > 0) currentRow.push(currentCell);
if (currentRow.length > 0) rows.push(currentRow);
currentRow = [];
currentCell = '';
if (char === '\r' && nextChar === '\n') i++;
} else {
currentCell += char;
}
}
if (currentCell || currentRow.length > 0) {
currentRow.push(currentCell);
rows.push(currentRow);
}
return rows;
}
export interface ExtractionResult {
items: ExtractedItem[];
ignored: string[][];
}
export function extractItemsFromCSV(csvText: string): ExtractionResult {
const rows = parseCSV(csvText);
const extractedItems: ExtractedItem[] = [];
const ignored: string[][] = [];
const regexInline = /^Item\s*:\s*([\s\S]*?)\s*\(\s*([\d,.]+)\s*\).*$/i;
rows.forEach(row => {
let matched = false;
row.forEach((cell, index) => {
if (cell && typeof cell === 'string' && cell.trim().toLowerCase().startsWith('item')) {
const cellContent = cell.trim();
const match = cellContent.match(regexInline);
if (match) {
let desc = match[1].trim().replace(/,\s*$/, '').trim();
extractedItems.push({
id: crypto.randomUUID(),
description: desc,
quantity: match[2].trim()
});
matched = true;
} else {
let description = cellContent.replace(/^Item\s*:\s*/i, '').trim().replace(/,\s*$/, '').trim();
let quantity = '';
if (index + 1 < row.length) {
const nextCell = row[index + 1];
if (nextCell) {
const nextCellStr = nextCell.toString().trim();
const qtyMatch = nextCellStr.match(/[\d,.]+/);
if (qtyMatch) {
quantity = qtyMatch[0];
}
}
}
extractedItems.push({
id: crypto.randomUUID(),
description,
quantity: quantity || ""
});
matched = true;
}
}
});
if (!matched) {
// Check if row is not just empty strings
if (row.some(cell => cell.trim() !== '')) {
ignored.push(row);
}
}
});
return { items: extractedItems, ignored };
}
File diff suppressed because one or more lines are too long
+87
View File
@@ -0,0 +1,87 @@
export interface ExtractedItem {
id: string;
description: string;
quantity: string;
}
export interface ExtractionResult {
items: ExtractedItem[];
ignored: string[][];
}
/**
* Parses raw XML text into a 2D array of strings based on <LINE> and <CELL> tags.
*/
export function parseXML(text: string): string[][] {
const rows: string[][] = [];
const lines = text.split('</LINE>');
for (const line of lines) {
if (!line.includes('<LINE>')) continue;
// Extract content inside <LINE> ... </LINE>
const lineContent = line.substring(line.indexOf('<LINE>') + 6);
const cells = lineContent.split('</CELL>');
const currentRow: string[] = [];
for (const cell of cells) {
if (!cell.includes('<CELL')) continue;
// Extract content inside <CELL ...> ... </CELL>
const cellContent = cell.substring(cell.indexOf('>') + 1);
currentRow.push(cellContent.trim());
}
if (currentRow.length > 0) {
rows.push(currentRow);
}
}
return rows;
}
/**
* Extracts items from the parsed XML grid, matching the CSV parser's output structure.
*/
export function extractItemsFromXML(xmlText: string): ExtractionResult {
const rows = parseXML(xmlText);
const extractedItems: ExtractedItem[] = [];
const ignored: string[][] = [];
rows.forEach(row => {
let matched = false;
// Valid data lines in the provided XML have at least 8 columns.
// Index 6 maps to "Item", Index 7 maps to "Order" (Quantity)
if (row.length >= 8) {
const itemDesc = row[6];
const quantityStr = row[7];
// Filter out the header row ('Item') and empty/layout spacer rows ('.')
if (itemDesc && quantityStr && itemDesc !== 'Item' && itemDesc !== '.') {
const qtyMatch = quantityStr.match(/[\d,.]+/);
if (qtyMatch) {
// Remove trailing commas, identically to the CSV parser logic
const desc = itemDesc.replace(/,\s*$/, '').trim();
extractedItems.push({
id: crypto.randomUUID(),
description: desc,
quantity: qtyMatch[0]
});
matched = true;
}
}
}
if (!matched) {
// Check if row is not just empty strings, mimicking original logic
if (row.some(cell => cell !== '')) {
ignored.push(row);
}
}
});
return { items: extractedItems, ignored };
}