Fix chart icon display issues
Fixes icon placement, click propagation, and missing total revenue/profit graphs. Addresses alignment and layout issues.
This commit is contained in:
@@ -1,4 +1,3 @@
|
|||||||
|
|
||||||
import { CardTitle } from '@/components/ui/card';
|
import { CardTitle } from '@/components/ui/card';
|
||||||
import { Button } from '@/components/ui/button';
|
import { Button } from '@/components/ui/button';
|
||||||
import { Copy, BarChart3 } from 'lucide-react';
|
import { Copy, BarChart3 } from 'lucide-react';
|
||||||
@@ -29,6 +28,8 @@ const JobCardHeader: React.FC<JobCardHeaderProps> = ({
|
|||||||
const { copying, copyToClipboard } = useClipboard();
|
const { copying, copyToClipboard } = useClipboard();
|
||||||
const { jobs } = useJobs();
|
const { jobs } = useJobs();
|
||||||
const [overviewChartOpen, setOverviewChartOpen] = useState(false);
|
const [overviewChartOpen, setOverviewChartOpen] = useState(false);
|
||||||
|
const [totalRevenueChartOpen, setTotalRevenueChartOpen] = useState(false);
|
||||||
|
const [totalProfitChartOpen, setTotalProfitChartOpen] = useState(false);
|
||||||
|
|
||||||
const sortedIncome = [...job.income].sort((a, b) =>
|
const sortedIncome = [...job.income].sort((a, b) =>
|
||||||
new Date(b.date).getTime() - new Date(a.date).getTime()
|
new Date(b.date).getTime() - new Date(a.date).getTime()
|
||||||
@@ -60,18 +61,34 @@ const JobCardHeader: React.FC<JobCardHeaderProps> = ({
|
|||||||
<span className="ml-4">
|
<span className="ml-4">
|
||||||
Produced: <EditableProduced job={job} onUpdateProduced={onUpdateProduced} />
|
Produced: <EditableProduced job={job} onUpdateProduced={onUpdateProduced} />
|
||||||
</span>
|
</span>
|
||||||
<span className="ml-4 flex items-center gap-1">
|
<span className="ml-4">
|
||||||
Sold: <span className="text-green-400">{itemsSold.toLocaleString()}</span>
|
Sold: <span className="text-green-400">{itemsSold.toLocaleString()}</span>
|
||||||
<div
|
|
||||||
className="cursor-pointer hover:text-blue-300 transition-colors"
|
|
||||||
onClick={() => setOverviewChartOpen(true)}
|
|
||||||
data-no-navigate
|
|
||||||
title="View overview charts"
|
|
||||||
>
|
|
||||||
<BarChart3 className="w-3 h-3" />
|
|
||||||
</div>
|
|
||||||
</span>
|
</span>
|
||||||
</div>
|
</div>
|
||||||
|
<div className="flex items-center gap-2 text-xs">
|
||||||
|
<span className="text-gray-400">Total Revenue</span>
|
||||||
|
<button
|
||||||
|
className="text-gray-400 hover:text-blue-300 transition-colors"
|
||||||
|
onClick={(e) => {
|
||||||
|
e.stopPropagation();
|
||||||
|
setTotalRevenueChartOpen(true);
|
||||||
|
}}
|
||||||
|
data-no-navigate
|
||||||
|
>
|
||||||
|
<BarChart3 className="w-3 h-3" />
|
||||||
|
</button>
|
||||||
|
<span className="text-gray-400 ml-2">Total Profit</span>
|
||||||
|
<button
|
||||||
|
className="text-gray-400 hover:text-blue-300 transition-colors"
|
||||||
|
onClick={(e) => {
|
||||||
|
e.stopPropagation();
|
||||||
|
setTotalProfitChartOpen(true);
|
||||||
|
}}
|
||||||
|
data-no-navigate
|
||||||
|
>
|
||||||
|
<BarChart3 className="w-3 h-3" />
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div className="flex flex-col gap-2 flex-shrink-0 items-end">
|
<div className="flex flex-col gap-2 flex-shrink-0 items-end">
|
||||||
@@ -104,8 +121,22 @@ const JobCardHeader: React.FC<JobCardHeaderProps> = ({
|
|||||||
isOpen={overviewChartOpen}
|
isOpen={overviewChartOpen}
|
||||||
onClose={() => setOverviewChartOpen(false)}
|
onClose={() => setOverviewChartOpen(false)}
|
||||||
/>
|
/>
|
||||||
|
|
||||||
|
<TransactionChart
|
||||||
|
jobs={jobs}
|
||||||
|
type="total-revenue"
|
||||||
|
isOpen={totalRevenueChartOpen}
|
||||||
|
onClose={() => setTotalRevenueChartOpen(false)}
|
||||||
|
/>
|
||||||
|
|
||||||
|
<TransactionChart
|
||||||
|
jobs={jobs}
|
||||||
|
type="total-profit"
|
||||||
|
isOpen={totalProfitChartOpen}
|
||||||
|
onClose={() => setTotalProfitChartOpen(false)}
|
||||||
|
/>
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
export default JobCardHeader;
|
export default JobCardHeader;
|
@@ -80,7 +80,8 @@ const JobCardMetrics: React.FC<JobCardMetricsProps> = ({ job }) => {
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
const openChart = (type: 'costs' | 'revenue' | 'profit') => {
|
const openChart = (type: 'costs' | 'revenue' | 'profit', e: React.MouseEvent) => {
|
||||||
|
e.stopPropagation();
|
||||||
setChartModal({ type, isOpen: true });
|
setChartModal({ type, isOpen: true });
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -91,13 +92,15 @@ const JobCardMetrics: React.FC<JobCardMetricsProps> = ({ job }) => {
|
|||||||
return (
|
return (
|
||||||
<div className="grid grid-cols-3 gap-3 pt-4 border-t border-gray-700/50 flex-shrink-0">
|
<div className="grid grid-cols-3 gap-3 pt-4 border-t border-gray-700/50 flex-shrink-0">
|
||||||
<div className="text-center space-y-1">
|
<div className="text-center space-y-1">
|
||||||
<div className="text-xs font-medium text-red-400 uppercase tracking-wide flex items-center gap-1">
|
<div className="text-xs font-medium text-red-400 uppercase tracking-wide flex items-center justify-center gap-1">
|
||||||
Costs
|
Costs
|
||||||
<BarChart3
|
<button
|
||||||
className="w-3 h-3 cursor-pointer hover:text-red-300 transition-colors"
|
className="hover:text-red-300 transition-colors"
|
||||||
onClick={() => openChart('costs')}
|
onClick={(e) => openChart('costs', e)}
|
||||||
data-no-navigate
|
data-no-navigate
|
||||||
/>
|
>
|
||||||
|
<BarChart3 className="w-3 h-3" />
|
||||||
|
</button>
|
||||||
</div>
|
</div>
|
||||||
<JobTransactionPopover job={job} type="costs">
|
<JobTransactionPopover job={job} type="costs">
|
||||||
<div className="text-lg font-bold text-red-400 cursor-pointer hover:text-red-300 transition-colors" data-no-navigate>
|
<div className="text-lg font-bold text-red-400 cursor-pointer hover:text-red-300 transition-colors" data-no-navigate>
|
||||||
@@ -139,13 +142,15 @@ const JobCardMetrics: React.FC<JobCardMetricsProps> = ({ job }) => {
|
|||||||
)}
|
)}
|
||||||
</div>
|
</div>
|
||||||
<div className="text-center space-y-1">
|
<div className="text-center space-y-1">
|
||||||
<div className="text-xs font-medium text-green-400 uppercase tracking-wide flex items-center gap-1">
|
<div className="text-xs font-medium text-green-400 uppercase tracking-wide flex items-center justify-center gap-1">
|
||||||
Revenue
|
Revenue
|
||||||
<BarChart3
|
<button
|
||||||
className="w-3 h-3 cursor-pointer hover:text-green-300 transition-colors"
|
className="hover:text-green-300 transition-colors"
|
||||||
onClick={() => openChart('revenue')}
|
onClick={(e) => openChart('revenue', e)}
|
||||||
data-no-navigate
|
data-no-navigate
|
||||||
/>
|
>
|
||||||
|
<BarChart3 className="w-3 h-3" />
|
||||||
|
</button>
|
||||||
</div>
|
</div>
|
||||||
<JobTransactionPopover job={job} type="revenue">
|
<JobTransactionPopover job={job} type="revenue">
|
||||||
<div className="text-lg font-bold text-green-400 cursor-pointer hover:text-green-300 transition-colors" data-no-navigate>
|
<div className="text-lg font-bold text-green-400 cursor-pointer hover:text-green-300 transition-colors" data-no-navigate>
|
||||||
@@ -202,13 +207,15 @@ const JobCardMetrics: React.FC<JobCardMetricsProps> = ({ job }) => {
|
|||||||
)}
|
)}
|
||||||
</div>
|
</div>
|
||||||
<div className="text-center space-y-1">
|
<div className="text-center space-y-1">
|
||||||
<div className="text-xs font-medium text-gray-300 uppercase tracking-wide flex items-center gap-1">
|
<div className="text-xs font-medium text-gray-300 uppercase tracking-wide flex items-center justify-center gap-1">
|
||||||
Profit
|
Profit
|
||||||
<BarChart3
|
<button
|
||||||
className="w-3 h-3 cursor-pointer hover:text-gray-100 transition-colors"
|
className="hover:text-gray-100 transition-colors"
|
||||||
onClick={() => openChart('profit')}
|
onClick={(e) => openChart('profit', e)}
|
||||||
data-no-navigate
|
data-no-navigate
|
||||||
/>
|
>
|
||||||
|
<BarChart3 className="w-3 h-3" />
|
||||||
|
</button>
|
||||||
</div>
|
</div>
|
||||||
<JobTransactionPopover job={job} type="profit">
|
<JobTransactionPopover job={job} type="profit">
|
||||||
<div className={`text-lg font-bold cursor-pointer transition-colors ${profit >= 0 ? 'text-green-400 hover:text-green-300' : 'text-red-400 hover:text-red-300'}`} data-no-navigate>
|
<div className={`text-lg font-bold cursor-pointer transition-colors ${profit >= 0 ? 'text-green-400 hover:text-green-300' : 'text-red-400 hover:text-red-300'}`} data-no-navigate>
|
||||||
@@ -235,4 +242,4 @@ const JobCardMetrics: React.FC<JobCardMetricsProps> = ({ job }) => {
|
|||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
export default JobCardMetrics;
|
export default JobCardMetrics;
|
@@ -9,7 +9,7 @@ import { format, parseISO } from 'date-fns';
|
|||||||
interface TransactionChartProps {
|
interface TransactionChartProps {
|
||||||
job?: IndJob;
|
job?: IndJob;
|
||||||
jobs?: IndJob[];
|
jobs?: IndJob[];
|
||||||
type: 'costs' | 'revenue' | 'profit' | 'overview';
|
type: 'costs' | 'revenue' | 'profit' | 'overview' | 'total-revenue' | 'total-profit';
|
||||||
isOpen: boolean;
|
isOpen: boolean;
|
||||||
onClose: () => void;
|
onClose: () => void;
|
||||||
}
|
}
|
||||||
@@ -95,6 +95,8 @@ const TransactionChart: React.FC<TransactionChartProps> = ({
|
|||||||
|
|
||||||
const getTitle = () => {
|
const getTitle = () => {
|
||||||
if (type === 'overview') return 'Overview - Revenue & Profit Over Time';
|
if (type === 'overview') return 'Overview - Revenue & Profit Over Time';
|
||||||
|
if (type === 'total-revenue') return 'Total Revenue Over Time';
|
||||||
|
if (type === 'total-profit') return 'Total Profit Over Time';
|
||||||
if (job) {
|
if (job) {
|
||||||
switch (type) {
|
switch (type) {
|
||||||
case 'costs': return `${job.outputItem} - Costs Over Time`;
|
case 'costs': return `${job.outputItem} - Costs Over Time`;
|
||||||
@@ -107,7 +109,7 @@ const TransactionChart: React.FC<TransactionChartProps> = ({
|
|||||||
};
|
};
|
||||||
|
|
||||||
const renderChart = () => {
|
const renderChart = () => {
|
||||||
if (type === 'overview') {
|
if (type === 'overview' || type === 'total-revenue' || type === 'total-profit') {
|
||||||
return (
|
return (
|
||||||
<AreaChart data={data} margin={{ top: 20, right: 30, left: 20, bottom: 5 }}>
|
<AreaChart data={data} margin={{ top: 20, right: 30, left: 20, bottom: 5 }}>
|
||||||
<CartesianGrid strokeDasharray="3 3" stroke="#374151" />
|
<CartesianGrid strokeDasharray="3 3" stroke="#374151" />
|
||||||
@@ -119,24 +121,28 @@ const TransactionChart: React.FC<TransactionChartProps> = ({
|
|||||||
contentStyle={{ backgroundColor: '#1F2937', border: '1px solid #374151' }}
|
contentStyle={{ backgroundColor: '#1F2937', border: '1px solid #374151' }}
|
||||||
/>
|
/>
|
||||||
<Legend />
|
<Legend />
|
||||||
<Area
|
{(type === 'overview' || type === 'total-revenue') && (
|
||||||
type="monotone"
|
<Area
|
||||||
dataKey="revenue"
|
type="monotone"
|
||||||
stackId="1"
|
dataKey="revenue"
|
||||||
stroke="#10B981"
|
stackId="1"
|
||||||
fill="#10B981"
|
stroke="#10B981"
|
||||||
fillOpacity={0.6}
|
fill="#10B981"
|
||||||
name="Revenue"
|
fillOpacity={0.6}
|
||||||
/>
|
name="Revenue"
|
||||||
<Area
|
/>
|
||||||
type="monotone"
|
)}
|
||||||
dataKey="profit"
|
{(type === 'overview' || type === 'total-profit') && (
|
||||||
stackId="2"
|
<Area
|
||||||
stroke="#3B82F6"
|
type="monotone"
|
||||||
fill="#3B82F6"
|
dataKey="profit"
|
||||||
fillOpacity={0.6}
|
stackId="2"
|
||||||
name="Profit"
|
stroke="#3B82F6"
|
||||||
/>
|
fill="#3B82F6"
|
||||||
|
fillOpacity={0.6}
|
||||||
|
name="Profit"
|
||||||
|
/>
|
||||||
|
)}
|
||||||
</AreaChart>
|
</AreaChart>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
@@ -1,6 +1,9 @@
|
|||||||
import PocketBase from 'pocketbase';
|
import PocketBase from 'pocketbase';
|
||||||
import { TypedPocketBase } from './pbtypes';
|
import { TypedPocketBase } from './pbtypes';
|
||||||
import { POCKETBASE_SUPERUSER_EMAIL, POCKETBASE_SUPERUSER_PASSWORD } from './pocketbaseAdmin';
|
|
||||||
|
// Admin credentials for PocketBase
|
||||||
|
const POCKETBASE_SUPERUSER_EMAIL = 'admin@admin.com';
|
||||||
|
const POCKETBASE_SUPERUSER_PASSWORD = 'admin1234567890';
|
||||||
|
|
||||||
const pb = new PocketBase('https://evebase.site.quack-lab.dev') as TypedPocketBase;
|
const pb = new PocketBase('https://evebase.site.quack-lab.dev') as TypedPocketBase;
|
||||||
|
|
||||||
|
Reference in New Issue
Block a user