diff --git a/src/components/JobCardHeader.tsx b/src/components/JobCardHeader.tsx index f6cbb51..620c9d3 100644 --- a/src/components/JobCardHeader.tsx +++ b/src/components/JobCardHeader.tsx @@ -61,8 +61,19 @@ const JobCardHeader: React.FC = ({ Produced: - + Sold: {itemsSold.toLocaleString()} +
@@ -116,8 +127,8 @@ const JobCardHeader: React.FC = ({
setOverviewChartOpen(false)} /> diff --git a/src/components/TransactionChart.tsx b/src/components/TransactionChart.tsx index 7ec89ea..56e3745 100644 --- a/src/components/TransactionChart.tsx +++ b/src/components/TransactionChart.tsx @@ -33,6 +33,25 @@ const TransactionChart: React.FC = ({ setHiddenLines(newHidden); }; + const getSmartTimeFormat = (transactions: any[]) => { + if (transactions.length === 0) return 'day'; + + // Calculate time span + const dates = transactions.map(tx => new Date(tx.date)); + const minDate = new Date(Math.min(...dates.map(d => d.getTime()))); + const maxDate = new Date(Math.max(...dates.map(d => d.getTime()))); + const timeSpanDays = (maxDate.getTime() - minDate.getTime()) / (1000 * 60 * 60 * 24); + + // Calculate transaction density per day + const transactionsPerDay = transactions.length / Math.max(timeSpanDays, 1); + + // Smart scoping: if many transactions per day, use hourly; otherwise daily + if (transactionsPerDay > 10 && timeSpanDays < 7) { + return 'hour'; + } + return 'day'; + }; + const getJobChartData = (job: IndJob) => { // Combine all transactions and group by date const allTransactions = [ @@ -40,11 +59,16 @@ const TransactionChart: React.FC = ({ ...job.income.map(tx => ({ ...tx, type: 'income' })) ]; - // Group by date + const timeFormat = getSmartTimeFormat(allTransactions); + + // Group by appropriate time unit const dateMap = new Map(); allTransactions.forEach(tx => { - const dateStr = format(parseISO(tx.date), 'yyyy-MM-dd'); + const dateStr = timeFormat === 'hour' + ? format(parseISO(tx.date), 'yyyy-MM-dd HH:00') + : format(parseISO(tx.date), 'yyyy-MM-dd'); + if (!dateMap.has(dateStr)) { dateMap.set(dateStr, { costs: 0, revenue: 0, date: dateStr }); } @@ -61,30 +85,46 @@ const TransactionChart: React.FC = ({ .map(entry => ({ ...entry, profit: entry.revenue - entry.costs, - formattedDate: format(new Date(entry.date), 'MMM dd') + formattedDate: timeFormat === 'hour' + ? format(new Date(entry.date), 'MMM dd HH:mm') + : format(new Date(entry.date), 'MMM dd') })) .sort((a, b) => a.date.localeCompare(b.date)); }; const getOverviewChartData = (jobs: IndJob[]) => { + // Combine all transactions from all jobs + const allTransactions = jobs.flatMap(job => [ + ...job.expenditures.map(tx => ({ ...tx, type: 'expenditure' })), + ...job.income.map(tx => ({ ...tx, type: 'income' })) + ]); + + const timeFormat = getSmartTimeFormat(allTransactions); const dateMap = new Map(); - jobs.forEach(job => { - const jobData = getJobChartData(job); - jobData.forEach(entry => { - if (!dateMap.has(entry.date)) { - dateMap.set(entry.date, { revenue: 0, profit: 0, date: entry.date }); - } - const overviewEntry = dateMap.get(entry.date)!; - overviewEntry.revenue += entry.revenue; - overviewEntry.profit += entry.profit; - }); + allTransactions.forEach(tx => { + const dateStr = timeFormat === 'hour' + ? format(parseISO(tx.date), 'yyyy-MM-dd HH:00') + : format(parseISO(tx.date), 'yyyy-MM-dd'); + + if (!dateMap.has(dateStr)) { + dateMap.set(dateStr, { revenue: 0, profit: 0, date: dateStr }); + } + const entry = dateMap.get(dateStr)!; + if (tx.type === 'income') { + entry.revenue += tx.totalPrice; + entry.profit += tx.totalPrice; + } else { + entry.profit -= tx.totalPrice; + } }); return Array.from(dateMap.values()) .map(entry => ({ ...entry, - formattedDate: format(new Date(entry.date), 'MMM dd') + formattedDate: timeFormat === 'hour' + ? format(new Date(entry.date), 'MMM dd HH:mm') + : format(new Date(entry.date), 'MMM dd') })) .sort((a, b) => a.date.localeCompare(b.date)); }; @@ -111,10 +151,10 @@ const TransactionChart: React.FC = ({ const renderChart = () => { if (type === 'overview' || type === 'total-revenue' || type === 'total-profit') { return ( - + - + = ({ if (type === 'costs') { return ( - + - + = ({ if (type === 'revenue') { return ( - + - + = ({ // Combined profit chart (costs, revenue, profit) return ( - + - + = ({ }; return ( - + { + if (!open) { + onClose(); + } + }}> {getTitle()} @@ -254,7 +298,15 @@ const TransactionChart: React.FC = ({
-