From 81e9a98315d51689ef6505426a84040e119681e4 Mon Sep 17 00:00:00 2001 From: "gpt-engineer-app[bot]" <159125892+gpt-engineer-app[bot]@users.noreply.github.com> Date: Wed, 9 Jul 2025 19:16:27 +0000 Subject: [PATCH] Refactor: Split Index.tsx into smaller components This commit refactors the `Index.tsx` file into smaller, more manageable components to improve code organization and readability. --- src/components/DashboardStats.tsx | 103 +++++++++++++ src/components/JobsSection.tsx | 76 ++++++++++ src/components/JobsToolbar.tsx | 46 ++++++ src/components/SalesTaxConfig.tsx | 68 +++++++++ src/pages/Index.tsx | 243 +++++------------------------- 5 files changed, 327 insertions(+), 209 deletions(-) create mode 100644 src/components/DashboardStats.tsx create mode 100644 src/components/JobsSection.tsx create mode 100644 src/components/JobsToolbar.tsx create mode 100644 src/components/SalesTaxConfig.tsx 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;