diff --git a/src/components/DashboardStats.tsx b/src/components/DashboardStats.tsx
new file mode 100644
index 0000000..928b0f2
--- /dev/null
+++ b/src/components/DashboardStats.tsx
@@ -0,0 +1,103 @@
+
+import { Card, CardContent, CardHeader, CardTitle } from '@/components/ui/card';
+import { Button } from '@/components/ui/button';
+import { Factory, TrendingUp, Briefcase, BarChart3 } from 'lucide-react';
+import { formatISK } from '@/utils/priceUtils';
+import RecapPopover from './RecapPopover';
+import { IndJob } from '@/lib/types';
+
+interface DashboardStatsProps {
+ totalJobs: number;
+ totalRevenue: number;
+ totalProfit: number;
+ jobs: IndJob[];
+ calculateJobRevenue: (job: IndJob) => number;
+ calculateJobProfit: (job: IndJob) => number;
+ onTotalRevenueChart: () => void;
+ onTotalProfitChart: () => void;
+}
+
+const DashboardStats = ({
+ totalJobs,
+ totalRevenue,
+ totalProfit,
+ jobs,
+ calculateJobRevenue,
+ calculateJobProfit,
+ onTotalRevenueChart,
+ onTotalProfitChart
+}: DashboardStatsProps) => {
+ return (
+
+
+
+
+
+ Active Jobs
+
+
+
+ {totalJobs}
+
+
+
+
+
+
+
+ Total Revenue
+
+
+
+
+
+
+ {formatISK(totalRevenue)}
+
+
+
+
+
+
+
+
+
+ Total Profit
+
+
+
+
+
+ = 0 ? 'text-green-400 hover:text-green-300' : 'text-red-400 hover:text-red-300'}`}>
+ {formatISK(totalProfit)}
+
+
+
+
+
+ );
+};
+
+export default DashboardStats;
diff --git a/src/components/JobsSection.tsx b/src/components/JobsSection.tsx
new file mode 100644
index 0000000..b399719
--- /dev/null
+++ b/src/components/JobsSection.tsx
@@ -0,0 +1,76 @@
+
+import { IndJob } from '@/lib/types';
+import JobGroup from './JobGroup';
+
+interface JobsSectionProps {
+ regularJobs: IndJob[];
+ trackedJobs: IndJob[];
+ collapsedGroups: Record;
+ loadingStatuses: Set;
+ onToggleGroup: (status: string) => void;
+ onEdit: (job: IndJob) => void;
+ onDelete: (jobId: string) => void;
+ onUpdateProduced: (jobId: string, produced: number) => void;
+ onImportBOM: (jobId: string, items: { name: string; quantity: number }[]) => void;
+}
+
+const JobsSection = ({
+ regularJobs,
+ trackedJobs,
+ collapsedGroups,
+ loadingStatuses,
+ onToggleGroup,
+ onEdit,
+ onDelete,
+ onUpdateProduced,
+ onImportBOM
+}: JobsSectionProps) => {
+ const jobGroups = regularJobs.reduce((groups, job) => {
+ const status = job.status;
+ if (!groups[status]) {
+ groups[status] = [];
+ }
+ groups[status].push(job);
+ return groups;
+ }, {} as Record);
+
+ return (
+
+
+ {Object.entries(jobGroups).map(([status, statusJobs]) => (
+
+ ))}
+
+
+ {trackedJobs.length > 0 && (
+
+
+
+ )}
+
+ );
+};
+
+export default JobsSection;
diff --git a/src/components/JobsToolbar.tsx b/src/components/JobsToolbar.tsx
new file mode 100644
index 0000000..72b7705
--- /dev/null
+++ b/src/components/JobsToolbar.tsx
@@ -0,0 +1,46 @@
+
+import { Button } from '@/components/ui/button';
+import { Plus, FileText, ShoppingCart } from 'lucide-react';
+import SalesTaxConfig from './SalesTaxConfig';
+
+interface JobsToolbarProps {
+ onNewJob: () => void;
+ onBatchIncome: () => void;
+ onBatchExpenditure: () => void;
+}
+
+const JobsToolbar = ({ onNewJob, onBatchIncome, onBatchExpenditure }: JobsToolbarProps) => {
+ return (
+
+
Jobs
+
+
+
+
+
+
+
+ );
+};
+
+export default JobsToolbar;
diff --git a/src/components/SalesTaxConfig.tsx b/src/components/SalesTaxConfig.tsx
new file mode 100644
index 0000000..85fd2fe
--- /dev/null
+++ b/src/components/SalesTaxConfig.tsx
@@ -0,0 +1,68 @@
+
+import { useState } from 'react';
+import { Button } from '@/components/ui/button';
+import { Input } from '@/components/ui/input';
+import { Label } from '@/components/ui/label';
+import { Popover, PopoverContent, PopoverTrigger } from '@/components/ui/popover';
+import { Settings } from 'lucide-react';
+
+const SalesTaxConfig = () => {
+ const [salesTax, setSalesTax] = useState(() => {
+ return localStorage.getItem('salesTax') || '0';
+ });
+ const [isOpen, setIsOpen] = useState(false);
+
+ const handleSave = () => {
+ localStorage.setItem('salesTax', salesTax);
+ setIsOpen(false);
+ window.dispatchEvent(new StorageEvent('storage', {
+ key: 'salesTax',
+ newValue: salesTax
+ }));
+ };
+
+ return (
+
+
+
+
+
+
+
+
+
setSalesTax(e.target.value)}
+ onBlur={handleSave}
+ onKeyDown={(e) => {
+ if (e.key === 'Enter') {
+ handleSave();
+ }
+ }}
+ placeholder="0"
+ min="0"
+ max="100"
+ step="0.1"
+ className="bg-gray-800 border-gray-600 text-white"
+ />
+
+ Applied to minimum price calculations
+
+
+
+
+
+ );
+};
+
+export default SalesTaxConfig;
diff --git a/src/pages/Index.tsx b/src/pages/Index.tsx
index 932cdb3..693a557 100644
--- a/src/pages/Index.tsx
+++ b/src/pages/Index.tsx
@@ -1,23 +1,18 @@
+
import { useState, useEffect, useRef } from 'react';
-import { Button } from '@/components/ui/button';
-import { Card, CardContent, CardHeader, CardTitle } from '@/components/ui/card';
-import { Input } from '@/components/ui/input';
-import { Label } from '@/components/ui/label';
-import { Popover, PopoverContent, PopoverTrigger } from '@/components/ui/popover';
-import { Plus, Factory, TrendingUp, Briefcase, FileText, Settings, BarChart3, ShoppingCart } from 'lucide-react';
import { IndTransactionRecordNoId, IndJobRecordNoId } from '@/lib/pbtypes';
-import { formatISK } from '@/utils/priceUtils';
import { getStatusPriority } from '@/utils/jobStatusUtils';
import JobForm from '@/components/JobForm';
-import JobGroup from '@/components/JobGroup';
import { IndJob } from '@/lib/types';
import BatchTransactionForm from '@/components/BatchTransactionForm';
import BatchExpenditureForm from '@/components/BatchExpenditureForm';
import { useJobs } from '@/hooks/useDataService';
import { useJobMetrics } from '@/hooks/useJobMetrics';
import SearchOverlay from '@/components/SearchOverlay';
-import RecapPopover from '@/components/RecapPopover';
import TransactionChart from '@/components/TransactionChart';
+import DashboardStats from '@/components/DashboardStats';
+import JobsToolbar from '@/components/JobsToolbar';
+import JobsSection from '@/components/JobsSection';
const Index = () => {
const {
@@ -176,15 +171,6 @@ const Index = () => {
}
};
- const jobGroups = regularJobs.reduce((groups, job) => {
- const status = job.status;
- if (!groups[status]) {
- groups[status] = [];
- }
- groups[status].push(job);
- return groups;
- }, {} as Record);
-
const toggleGroup = async (status: string) => {
const currentScrollY = window.scrollY;
@@ -248,143 +234,41 @@ const Index = () => {
}}
onSearch={setSearchQuery}
/>
-
-
-
-
-
- Active Jobs
-
-
-
- {totalJobs}
-
-
-
-
-
-
- Total Revenue
-
-
-
-
-
-
- {formatISK(totalRevenue)}
-
-
-
-
-
-
-
-
- Total Profit
-
-
-
-
-
- = 0 ? 'text-green-400 hover:text-green-300' : 'text-red-400 hover:text-red-300'}`}>
- {formatISK(totalProfit)}
-
-
-
-
-
+
+ setTotalRevenueChartOpen(true)}
+ onTotalProfitChart={() => setTotalProfitChartOpen(true)}
+ />
-
-
Jobs
-
-
-
-
-
-
-
+
{
+ setEditingJob(null);
+ setShowJobForm(true);
+ }}
+ onBatchIncome={() => setShowBatchForm(true)}
+ onBatchExpenditure={() => setShowBatchExpenditureForm(true)}
+ />
-
- {Object.entries(jobGroups).map(([status, statusJobs]) => (
-
- ))}
-
+
- {trackedJobs.length > 0 && (
-
-
-
- )}
-
{showBatchForm && (
{
);
};
-const SalesTaxConfig = () => {
- const [salesTax, setSalesTax] = useState(() => {
- return localStorage.getItem('salesTax') || '0';
- });
- const [isOpen, setIsOpen] = useState(false);
-
- const handleSave = () => {
- localStorage.setItem('salesTax', salesTax);
- setIsOpen(false);
- window.dispatchEvent(new StorageEvent('storage', {
- key: 'salesTax',
- newValue: salesTax
- }));
- };
-
- return (
-
-
-
-
-
-
-
-
-
setSalesTax(e.target.value)}
- onBlur={handleSave}
- onKeyDown={(e) => {
- if (e.key === 'Enter') {
- handleSave();
- }
- }}
- placeholder="0"
- min="0"
- max="100"
- step="0.1"
- className="bg-gray-800 border-gray-600 text-white"
- />
-
- Applied to minimum price calculations
-
-
-
-
-
- );
-};
-
export default Index;