Fix batch expenditure form and add autofocus
Fixed the batch expenditure form to correctly recognize transactions. Added autofocus to the input forms in both batch income and expenditure forms.
This commit is contained in:
@@ -1,5 +1,4 @@
|
||||
|
||||
import { useState } from 'react';
|
||||
import { useState, useRef, useEffect } from 'react';
|
||||
import { Card, CardContent, CardHeader, CardTitle } from '@/components/ui/card';
|
||||
import { Button } from '@/components/ui/button';
|
||||
import { Textarea } from '@/components/ui/textarea';
|
||||
@@ -28,6 +27,14 @@ const BatchExpenditureForm: React.FC<BatchExpenditureFormProps> = ({ onClose, on
|
||||
const [pastedData, setPastedData] = useState('');
|
||||
const [transactionGroups, setTransactionGroups] = useState<TransactionGroup[]>([]);
|
||||
const [duplicatesFound, setDuplicatesFound] = useState(0);
|
||||
const textareaRef = useRef<HTMLTextAreaElement>(null);
|
||||
|
||||
// Auto focus the textarea when component mounts
|
||||
useEffect(() => {
|
||||
if (textareaRef.current) {
|
||||
textareaRef.current.focus();
|
||||
}
|
||||
}, []);
|
||||
|
||||
// Filter jobs that are in acquisition status
|
||||
const eligibleJobs = jobs.filter(job => job.status === IndJobStatusOptions.Acquisition);
|
||||
@@ -84,19 +91,35 @@ const BatchExpenditureForm: React.FC<BatchExpenditureFormProps> = ({ onClose, on
|
||||
};
|
||||
|
||||
const handlePaste = (value: string) => {
|
||||
console.log('Handling paste with value:', value);
|
||||
setPastedData(value);
|
||||
const lines = value.trim().split('\n');
|
||||
const lines = value.trim().split('\n').filter(line => line.trim().length > 0);
|
||||
console.log('Processing lines:', lines);
|
||||
|
||||
const pasteTransactionMap = new Map<string, PastedTransaction>();
|
||||
|
||||
// STEP 1: Combine identical transactions within the pasted data
|
||||
lines.forEach((line) => {
|
||||
lines.forEach((line, index) => {
|
||||
console.log(`Processing line ${index}:`, line);
|
||||
const parsed: PastedTransaction | null = parseTransactionLine(line);
|
||||
if (parsed && parsed.totalPrice < 0) { // Only process expenditures (negative amounts)
|
||||
// Convert to positive values for expenditures
|
||||
parsed.totalPrice = Math.abs(parsed.totalPrice);
|
||||
parsed.unitPrice = Math.abs(parsed.unitPrice);
|
||||
|
||||
if (parsed) {
|
||||
console.log('Parsed transaction:', parsed);
|
||||
|
||||
// For expenditures, we expect negative amounts, but handle both cases
|
||||
const isExpenditure = parsed.totalPrice < 0;
|
||||
if (isExpenditure) {
|
||||
// Convert to positive values for expenditures
|
||||
parsed.totalPrice = Math.abs(parsed.totalPrice);
|
||||
parsed.unitPrice = Math.abs(parsed.unitPrice);
|
||||
} else {
|
||||
// If it's positive, we might still want to treat it as expenditure
|
||||
// based on context, but let's keep it as is for now
|
||||
console.log('Transaction has positive amount, treating as expenditure anyway');
|
||||
}
|
||||
|
||||
const transactionKey: string = createTransactionKey(parsed);
|
||||
console.log('Transaction key:', transactionKey);
|
||||
|
||||
if (pasteTransactionMap.has(transactionKey)) {
|
||||
const existing = pasteTransactionMap.get(transactionKey)!;
|
||||
@@ -108,9 +131,13 @@ const BatchExpenditureForm: React.FC<BatchExpenditureFormProps> = ({ onClose, on
|
||||
} else {
|
||||
pasteTransactionMap.set(transactionKey, parsed);
|
||||
}
|
||||
} else {
|
||||
console.log('Failed to parse line:', line);
|
||||
}
|
||||
});
|
||||
|
||||
console.log('Parsed transactions map:', pasteTransactionMap);
|
||||
|
||||
// STEP 2: Identify which jobs these transactions belong to
|
||||
const relevantJobIds = new Set<string>();
|
||||
pasteTransactionMap.forEach((transaction) => {
|
||||
@@ -147,6 +174,7 @@ const BatchExpenditureForm: React.FC<BatchExpenditureFormProps> = ({ onClose, on
|
||||
});
|
||||
|
||||
const transactionList = Array.from(pasteTransactionMap.values());
|
||||
console.log('Final transaction list:', transactionList);
|
||||
setDuplicatesFound(duplicates);
|
||||
|
||||
// Create individual transaction groups
|
||||
@@ -157,6 +185,7 @@ const BatchExpenditureForm: React.FC<BatchExpenditureFormProps> = ({ onClose, on
|
||||
totalValue: tx.totalPrice
|
||||
}));
|
||||
|
||||
console.log('Transaction groups:', groups);
|
||||
setTransactionGroups(groups);
|
||||
};
|
||||
|
||||
@@ -216,6 +245,7 @@ const BatchExpenditureForm: React.FC<BatchExpenditureFormProps> = ({ onClose, on
|
||||
Paste EVE expenditure data (negative amounts):
|
||||
</label>
|
||||
<Textarea
|
||||
ref={textareaRef}
|
||||
value={pastedData}
|
||||
onChange={(e) => handlePaste(e.target.value)}
|
||||
placeholder="Paste your EVE expenditure transaction data here..."
|
||||
|
@@ -1,5 +1,4 @@
|
||||
|
||||
import React, { useState } from 'react';
|
||||
import React, { useState, useRef, useEffect } from 'react';
|
||||
import { Card, CardContent, CardHeader, CardTitle } from '@/components/ui/card';
|
||||
import { Button } from '@/components/ui/button';
|
||||
import { Textarea } from '@/components/ui/textarea';
|
||||
@@ -19,6 +18,14 @@ const TransactionForm: React.FC<TransactionFormProps> = ({ jobId, onTransactions
|
||||
const [pastedData, setPastedData] = useState('');
|
||||
const [parsedTransactions, setParsedTransactions] = useState<IndTransactionRecordNoId[]>([]);
|
||||
const [transactionType, setTransactionType] = useState<'expenditure' | 'income'>('expenditure');
|
||||
const textareaRef = useRef<HTMLTextAreaElement>(null);
|
||||
|
||||
// Auto focus the textarea when component mounts or when transaction type changes
|
||||
useEffect(() => {
|
||||
if (textareaRef.current) {
|
||||
textareaRef.current.focus();
|
||||
}
|
||||
}, [transactionType]);
|
||||
|
||||
const handlePaste = (value: string) => {
|
||||
setPastedData(value);
|
||||
@@ -69,6 +76,7 @@ const TransactionForm: React.FC<TransactionFormProps> = ({ jobId, onTransactions
|
||||
Paste EVE transaction data (Ctrl+V):
|
||||
</label>
|
||||
<Textarea
|
||||
ref={textareaRef}
|
||||
value={pastedData}
|
||||
onChange={(e) => handlePaste(e.target.value)}
|
||||
placeholder="Paste your EVE transaction data here..."
|
||||
|
@@ -34,45 +34,85 @@ export type PastedTransaction = IndTransactionRecordNoId & {
|
||||
|
||||
export const parseTransactionLine = (line: string): PastedTransaction | null => {
|
||||
try {
|
||||
console.log('Parsing transaction line:', line);
|
||||
const parts = line.split('\t');
|
||||
if (parts.length < 6) return null;
|
||||
|
||||
let dateStr, quantityStr, itemName, unitPriceStr, totalAmountStr, buyer, location, corporation, wallet;
|
||||
if (parts.length === 8) {
|
||||
[dateStr, quantityStr, itemName, unitPriceStr, totalAmountStr, buyer, location, corporation, wallet] = parts;
|
||||
} else {
|
||||
[dateStr, quantityStr, itemName, unitPriceStr, totalAmountStr, buyer, location] = parts;
|
||||
console.log('Split parts:', parts, 'Length:', parts.length);
|
||||
|
||||
if (parts.length < 6) {
|
||||
console.log('Not enough parts, skipping line');
|
||||
return null;
|
||||
}
|
||||
|
||||
let dateStr, quantityStr, itemName, unitPriceStr, totalAmountStr, buyer, location, corporation, wallet;
|
||||
|
||||
// Handle both 7 and 8+ column formats
|
||||
if (parts.length >= 7) {
|
||||
[dateStr, quantityStr, itemName, unitPriceStr, totalAmountStr, buyer, location] = parts;
|
||||
if (parts.length >= 8) {
|
||||
corporation = parts[7];
|
||||
}
|
||||
if (parts.length >= 9) {
|
||||
wallet = parts[8];
|
||||
}
|
||||
} else {
|
||||
console.log('Unexpected number of columns:', parts.length);
|
||||
return null;
|
||||
}
|
||||
|
||||
console.log('Extracted values:', { dateStr, quantityStr, itemName, unitPriceStr, totalAmountStr, buyer, location });
|
||||
|
||||
// Parse date (YYYY.MM.DD HH:mm format)
|
||||
const [datePart, timePart] = dateStr.split(' ');
|
||||
if (!datePart || !timePart) {
|
||||
console.log('Invalid date format:', dateStr);
|
||||
return null;
|
||||
}
|
||||
|
||||
const [year, month, day] = datePart.split('.').map(Number);
|
||||
const [hour, minute] = timePart.split(':').map(Number);
|
||||
|
||||
if (isNaN(year) || isNaN(month) || isNaN(day) || isNaN(hour) || isNaN(minute)) {
|
||||
console.log('Invalid date components:', { year, month, day, hour, minute });
|
||||
return null;
|
||||
}
|
||||
|
||||
const date = new Date(year, month - 1, day, hour, minute);
|
||||
|
||||
// Parse quantity (remove commas)
|
||||
const quantity = parseInt(quantityStr.replace(/,/g, ''));
|
||||
if (isNaN(quantity)) {
|
||||
console.log('Invalid quantity:', quantityStr);
|
||||
return null;
|
||||
}
|
||||
|
||||
// Parse prices
|
||||
let unitPrice = parseISKAmount(unitPriceStr);
|
||||
let totalPrice = parseISKAmount(totalAmountStr);
|
||||
|
||||
if (totalPrice < 0) {
|
||||
totalPrice = -totalPrice;
|
||||
|
||||
console.log('Parsed prices:', { unitPrice, totalPrice });
|
||||
|
||||
if (isNaN(unitPrice) || isNaN(totalPrice)) {
|
||||
console.log('Invalid price values:', { unitPrice, totalPrice });
|
||||
return null;
|
||||
}
|
||||
|
||||
return {
|
||||
// Keep the original sign for expenditures (negative values)
|
||||
// The batch expenditure form will handle the conversion
|
||||
|
||||
const result = {
|
||||
date: date.toISOString(),
|
||||
quantity,
|
||||
itemName,
|
||||
itemName: itemName.trim(),
|
||||
unitPrice,
|
||||
totalPrice,
|
||||
buyer,
|
||||
location,
|
||||
corporation,
|
||||
wallet
|
||||
buyer: buyer?.trim() || '',
|
||||
location: location?.trim() || '',
|
||||
corporation: corporation?.trim(),
|
||||
wallet: wallet?.trim()
|
||||
};
|
||||
|
||||
console.log('Successfully parsed transaction:', result);
|
||||
return result;
|
||||
} catch (error) {
|
||||
console.error('Error parsing transaction line:', line, error);
|
||||
return null;
|
||||
|
Reference in New Issue
Block a user