Refactor Index page
Refactor `src/pages/Index.tsx` into smaller components.
This commit is contained in:
92
src/hooks/useDashboard.ts
Normal file
92
src/hooks/useDashboard.ts
Normal file
@@ -0,0 +1,92 @@
|
|||||||
|
|
||||||
|
import { useState, useEffect, useRef } from 'react';
|
||||||
|
import { IndJob } from '@/lib/types';
|
||||||
|
import { IndTransactionRecordNoId, IndJobRecordNoId } from '@/lib/pbtypes';
|
||||||
|
import { useJobs } from '@/hooks/useDataService';
|
||||||
|
|
||||||
|
export function useDashboard() {
|
||||||
|
const {
|
||||||
|
jobs,
|
||||||
|
loading,
|
||||||
|
error,
|
||||||
|
loadingStatuses,
|
||||||
|
createJob,
|
||||||
|
updateJob,
|
||||||
|
deleteJob,
|
||||||
|
createMultipleTransactions,
|
||||||
|
createMultipleBillItems,
|
||||||
|
loadJobsForStatuses
|
||||||
|
} = useJobs();
|
||||||
|
|
||||||
|
const [showJobForm, setShowJobForm] = useState(false);
|
||||||
|
const [editingJob, setEditingJob] = useState<IndJob | null>(null);
|
||||||
|
const [showBatchForm, setShowBatchForm] = useState(false);
|
||||||
|
const [showBatchExpenditureForm, setShowBatchExpenditureForm] = useState(false);
|
||||||
|
const [searchOpen, setSearchOpen] = useState(false);
|
||||||
|
const [searchQuery, setSearchQuery] = useState('');
|
||||||
|
const [totalRevenueChartOpen, setTotalRevenueChartOpen] = useState(false);
|
||||||
|
const [totalProfitChartOpen, setTotalProfitChartOpen] = useState(false);
|
||||||
|
const [collapsedGroups, setCollapsedGroups] = useState<Record<string, boolean>>(() => {
|
||||||
|
const saved = localStorage.getItem('jobGroupsCollapsed');
|
||||||
|
return saved ? JSON.parse(saved) : {};
|
||||||
|
});
|
||||||
|
|
||||||
|
const scrollPositionRef = useRef<number>(0);
|
||||||
|
const containerRef = useRef<HTMLDivElement>(null);
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
const handleKeyDown = (e: KeyboardEvent) => {
|
||||||
|
if ((e.ctrlKey || e.metaKey) && e.key === 'f') {
|
||||||
|
e.preventDefault();
|
||||||
|
setSearchOpen(true);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
window.addEventListener('keydown', handleKeyDown);
|
||||||
|
return () => window.removeEventListener('keydown', handleKeyDown);
|
||||||
|
}, []);
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
const handleScroll = () => {
|
||||||
|
scrollPositionRef.current = window.scrollY;
|
||||||
|
};
|
||||||
|
|
||||||
|
window.addEventListener('scroll', handleScroll);
|
||||||
|
return () => window.removeEventListener('scroll', handleScroll);
|
||||||
|
}, []);
|
||||||
|
|
||||||
|
return {
|
||||||
|
// State
|
||||||
|
jobs,
|
||||||
|
loading,
|
||||||
|
error,
|
||||||
|
loadingStatuses,
|
||||||
|
showJobForm,
|
||||||
|
setShowJobForm,
|
||||||
|
editingJob,
|
||||||
|
setEditingJob,
|
||||||
|
showBatchForm,
|
||||||
|
setShowBatchForm,
|
||||||
|
showBatchExpenditureForm,
|
||||||
|
setShowBatchExpenditureForm,
|
||||||
|
searchOpen,
|
||||||
|
setSearchOpen,
|
||||||
|
searchQuery,
|
||||||
|
setSearchQuery,
|
||||||
|
totalRevenueChartOpen,
|
||||||
|
setTotalRevenueChartOpen,
|
||||||
|
totalProfitChartOpen,
|
||||||
|
setTotalProfitChartOpen,
|
||||||
|
collapsedGroups,
|
||||||
|
setCollapsedGroups,
|
||||||
|
scrollPositionRef,
|
||||||
|
containerRef,
|
||||||
|
// Methods
|
||||||
|
createJob,
|
||||||
|
updateJob,
|
||||||
|
deleteJob,
|
||||||
|
createMultipleTransactions,
|
||||||
|
createMultipleBillItems,
|
||||||
|
loadJobsForStatuses
|
||||||
|
};
|
||||||
|
}
|
||||||
147
src/hooks/useDashboardHandlers.ts
Normal file
147
src/hooks/useDashboardHandlers.ts
Normal file
@@ -0,0 +1,147 @@
|
|||||||
|
|
||||||
|
import { IndJob } from '@/lib/types';
|
||||||
|
import { IndTransactionRecordNoId, IndJobRecordNoId } from '@/lib/pbtypes';
|
||||||
|
|
||||||
|
interface DashboardHandlersProps {
|
||||||
|
createJob: (jobData: IndJobRecordNoId) => Promise<IndJob>;
|
||||||
|
updateJob: (id: string, updates: Partial<IndJobRecordNoId>) => Promise<IndJob>;
|
||||||
|
deleteJob: (id: string) => Promise<void>;
|
||||||
|
createMultipleTransactions: (jobId: string, transactions: IndTransactionRecordNoId[], type: 'expenditure' | 'income') => Promise<IndJob>;
|
||||||
|
createMultipleBillItems: (jobId: string, items: { name: string; quantity: number; unitPrice: number }[], type: 'billOfMaterials' | 'consumedMaterials') => Promise<IndJob>;
|
||||||
|
loadJobsForStatuses: (statuses: string[]) => Promise<void>;
|
||||||
|
setShowJobForm: (show: boolean) => void;
|
||||||
|
setEditingJob: (job: IndJob | null) => void;
|
||||||
|
collapsedGroups: Record<string, boolean>;
|
||||||
|
setCollapsedGroups: (groups: Record<string, boolean>) => void;
|
||||||
|
loadingStatuses: Set<string>;
|
||||||
|
}
|
||||||
|
|
||||||
|
export function useDashboardHandlers({
|
||||||
|
createJob,
|
||||||
|
updateJob,
|
||||||
|
deleteJob,
|
||||||
|
createMultipleTransactions,
|
||||||
|
createMultipleBillItems,
|
||||||
|
loadJobsForStatuses,
|
||||||
|
setShowJobForm,
|
||||||
|
setEditingJob,
|
||||||
|
collapsedGroups,
|
||||||
|
setCollapsedGroups,
|
||||||
|
loadingStatuses
|
||||||
|
}: DashboardHandlersProps) {
|
||||||
|
|
||||||
|
const handleCreateJob = async (jobData: IndJobRecordNoId, billOfMaterials?: { name: string; quantity: number }[]) => {
|
||||||
|
try {
|
||||||
|
const newJob = await createJob(jobData);
|
||||||
|
|
||||||
|
if (billOfMaterials && billOfMaterials.length > 0) {
|
||||||
|
const billItems = billOfMaterials.map(item => ({
|
||||||
|
name: item.name,
|
||||||
|
quantity: item.quantity,
|
||||||
|
unitPrice: 0
|
||||||
|
}));
|
||||||
|
await createMultipleBillItems(newJob.id, billItems, 'billOfMaterials');
|
||||||
|
}
|
||||||
|
|
||||||
|
setShowJobForm(false);
|
||||||
|
} catch (error) {
|
||||||
|
console.error('Error creating job:', error);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
const handleEditJob = (job: IndJob) => {
|
||||||
|
setEditingJob(job);
|
||||||
|
setShowJobForm(true);
|
||||||
|
};
|
||||||
|
|
||||||
|
const handleUpdateJob = async (jobData: IndJobRecordNoId, editingJob: IndJob | null) => {
|
||||||
|
if (!editingJob) return;
|
||||||
|
|
||||||
|
try {
|
||||||
|
await updateJob(editingJob.id, jobData);
|
||||||
|
setShowJobForm(false);
|
||||||
|
setEditingJob(null);
|
||||||
|
} catch (error) {
|
||||||
|
console.error('Error updating job:', error);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
const handleDeleteJob = async (jobId: string) => {
|
||||||
|
if (confirm('Are you sure you want to delete this job?')) {
|
||||||
|
try {
|
||||||
|
await deleteJob(jobId);
|
||||||
|
} catch (error) {
|
||||||
|
console.error('Error deleting job:', error);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
const handleUpdateProduced = async (jobId: string, produced: number) => {
|
||||||
|
try {
|
||||||
|
await updateJob(jobId, { produced });
|
||||||
|
} catch (error) {
|
||||||
|
console.error('Error updating produced quantity:', error);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
const handleImportBOM = async (jobId: string, items: { name: string; quantity: number }[]) => {
|
||||||
|
try {
|
||||||
|
const billItems = items.map(item => ({
|
||||||
|
name: item.name,
|
||||||
|
quantity: item.quantity,
|
||||||
|
unitPrice: 0
|
||||||
|
}));
|
||||||
|
await createMultipleBillItems(jobId, billItems, 'billOfMaterials');
|
||||||
|
} catch (error) {
|
||||||
|
console.error('Error importing BOM:', error);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
const toggleGroup = async (status: string) => {
|
||||||
|
const currentScrollY = window.scrollY;
|
||||||
|
|
||||||
|
const newState = { ...collapsedGroups, [status]: !collapsedGroups[status] };
|
||||||
|
setCollapsedGroups(newState);
|
||||||
|
localStorage.setItem('jobGroupsCollapsed', JSON.stringify(newState));
|
||||||
|
|
||||||
|
if (collapsedGroups[status] && !loadingStatuses.has(status)) {
|
||||||
|
await loadJobsForStatuses([status]);
|
||||||
|
|
||||||
|
setTimeout(() => {
|
||||||
|
window.scrollTo(0, currentScrollY);
|
||||||
|
}, 50);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
const handleBatchTransactionsAssigned = async (assignments: { jobId: string, transactions: IndTransactionRecordNoId[] }[]) => {
|
||||||
|
try {
|
||||||
|
for (const { jobId, transactions } of assignments) {
|
||||||
|
await createMultipleTransactions(jobId, transactions, 'income');
|
||||||
|
}
|
||||||
|
} catch (error) {
|
||||||
|
console.error('Error assigning batch transactions:', error);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
const handleBatchExpendituresAssigned = async (assignments: { jobId: string, transactions: IndTransactionRecordNoId[] }[]) => {
|
||||||
|
try {
|
||||||
|
for (const { jobId, transactions } of assignments) {
|
||||||
|
await createMultipleTransactions(jobId, transactions, 'expenditure');
|
||||||
|
}
|
||||||
|
} catch (error) {
|
||||||
|
console.error('Error assigning batch expenditures:', error);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
return {
|
||||||
|
handleCreateJob,
|
||||||
|
handleEditJob,
|
||||||
|
handleUpdateJob,
|
||||||
|
handleDeleteJob,
|
||||||
|
handleUpdateProduced,
|
||||||
|
handleImportBOM,
|
||||||
|
toggleGroup,
|
||||||
|
handleBatchTransactionsAssigned,
|
||||||
|
handleBatchExpendituresAssigned
|
||||||
|
};
|
||||||
|
}
|
||||||
@@ -1,18 +1,16 @@
|
|||||||
|
|
||||||
import { useState, useEffect, useRef } from 'react';
|
|
||||||
import { IndTransactionRecordNoId, IndJobRecordNoId } from '@/lib/pbtypes';
|
|
||||||
import { getStatusPriority } from '@/utils/jobStatusUtils';
|
|
||||||
import JobForm from '@/components/JobForm';
|
import JobForm from '@/components/JobForm';
|
||||||
import { IndJob } from '@/lib/types';
|
|
||||||
import BatchTransactionForm from '@/components/BatchTransactionForm';
|
import BatchTransactionForm from '@/components/BatchTransactionForm';
|
||||||
import BatchExpenditureForm from '@/components/BatchExpenditureForm';
|
import BatchExpenditureForm from '@/components/BatchExpenditureForm';
|
||||||
import { useJobs } from '@/hooks/useDataService';
|
|
||||||
import { useJobMetrics } from '@/hooks/useJobMetrics';
|
|
||||||
import SearchOverlay from '@/components/SearchOverlay';
|
import SearchOverlay from '@/components/SearchOverlay';
|
||||||
import TransactionChart from '@/components/TransactionChart';
|
import TransactionChart from '@/components/TransactionChart';
|
||||||
import DashboardStats from '@/components/DashboardStats';
|
import DashboardStats from '@/components/DashboardStats';
|
||||||
import JobsToolbar from '@/components/JobsToolbar';
|
import JobsToolbar from '@/components/JobsToolbar';
|
||||||
import JobsSection from '@/components/JobsSection';
|
import JobsSection from '@/components/JobsSection';
|
||||||
|
import { useDashboard } from '@/hooks/useDashboard';
|
||||||
|
import { useDashboardHandlers } from '@/hooks/useDashboardHandlers';
|
||||||
|
import { useJobMetrics } from '@/hooks/useJobMetrics';
|
||||||
|
import { categorizeJobs } from '@/utils/jobFiltering';
|
||||||
|
|
||||||
const Index = () => {
|
const Index = () => {
|
||||||
const {
|
const {
|
||||||
@@ -20,52 +18,57 @@ const Index = () => {
|
|||||||
loading,
|
loading,
|
||||||
error,
|
error,
|
||||||
loadingStatuses,
|
loadingStatuses,
|
||||||
|
showJobForm,
|
||||||
|
setShowJobForm,
|
||||||
|
editingJob,
|
||||||
|
setEditingJob,
|
||||||
|
showBatchForm,
|
||||||
|
setShowBatchForm,
|
||||||
|
showBatchExpenditureForm,
|
||||||
|
setShowBatchExpenditureForm,
|
||||||
|
searchOpen,
|
||||||
|
setSearchOpen,
|
||||||
|
searchQuery,
|
||||||
|
setSearchQuery,
|
||||||
|
totalRevenueChartOpen,
|
||||||
|
setTotalRevenueChartOpen,
|
||||||
|
totalProfitChartOpen,
|
||||||
|
setTotalProfitChartOpen,
|
||||||
|
collapsedGroups,
|
||||||
|
setCollapsedGroups,
|
||||||
|
containerRef,
|
||||||
createJob,
|
createJob,
|
||||||
updateJob,
|
updateJob,
|
||||||
deleteJob,
|
deleteJob,
|
||||||
createMultipleTransactions,
|
createMultipleTransactions,
|
||||||
createMultipleBillItems,
|
createMultipleBillItems,
|
||||||
loadJobsForStatuses
|
loadJobsForStatuses
|
||||||
} = useJobs();
|
} = useDashboard();
|
||||||
|
|
||||||
const [showJobForm, setShowJobForm] = useState(false);
|
const {
|
||||||
const [editingJob, setEditingJob] = useState<IndJob | null>(null);
|
handleCreateJob,
|
||||||
const [showBatchForm, setShowBatchForm] = useState(false);
|
handleEditJob,
|
||||||
const [showBatchExpenditureForm, setShowBatchExpenditureForm] = useState(false);
|
handleUpdateJob,
|
||||||
const [searchOpen, setSearchOpen] = useState(false);
|
handleDeleteJob,
|
||||||
const [searchQuery, setSearchQuery] = useState('');
|
handleUpdateProduced,
|
||||||
const [totalRevenueChartOpen, setTotalRevenueChartOpen] = useState(false);
|
handleImportBOM,
|
||||||
const [totalProfitChartOpen, setTotalProfitChartOpen] = useState(false);
|
toggleGroup,
|
||||||
const [collapsedGroups, setCollapsedGroups] = useState<Record<string, boolean>>(() => {
|
handleBatchTransactionsAssigned,
|
||||||
const saved = localStorage.getItem('jobGroupsCollapsed');
|
handleBatchExpendituresAssigned
|
||||||
return saved ? JSON.parse(saved) : {};
|
} = useDashboardHandlers({
|
||||||
|
createJob,
|
||||||
|
updateJob,
|
||||||
|
deleteJob,
|
||||||
|
createMultipleTransactions,
|
||||||
|
createMultipleBillItems,
|
||||||
|
loadJobsForStatuses,
|
||||||
|
setShowJobForm,
|
||||||
|
setEditingJob,
|
||||||
|
collapsedGroups,
|
||||||
|
setCollapsedGroups,
|
||||||
|
loadingStatuses
|
||||||
});
|
});
|
||||||
|
|
||||||
// Track scroll position to prevent jarring jumps
|
|
||||||
const scrollPositionRef = useRef<number>(0);
|
|
||||||
const containerRef = useRef<HTMLDivElement>(null);
|
|
||||||
|
|
||||||
useEffect(() => {
|
|
||||||
const handleKeyDown = (e: KeyboardEvent) => {
|
|
||||||
if ((e.ctrlKey || e.metaKey) && e.key === 'f') {
|
|
||||||
e.preventDefault();
|
|
||||||
setSearchOpen(true);
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
window.addEventListener('keydown', handleKeyDown);
|
|
||||||
return () => window.removeEventListener('keydown', handleKeyDown);
|
|
||||||
}, []);
|
|
||||||
|
|
||||||
useEffect(() => {
|
|
||||||
const handleScroll = () => {
|
|
||||||
scrollPositionRef.current = window.scrollY;
|
|
||||||
};
|
|
||||||
|
|
||||||
window.addEventListener('scroll', handleScroll);
|
|
||||||
return () => window.removeEventListener('scroll', handleScroll);
|
|
||||||
}, []);
|
|
||||||
|
|
||||||
if (loading) {
|
if (loading) {
|
||||||
return (
|
return (
|
||||||
<div className="min-h-screen bg-gray-950 p-6 flex items-center justify-center">
|
<div className="min-h-screen bg-gray-950 p-6 flex items-center justify-center">
|
||||||
@@ -82,138 +85,16 @@ const Index = () => {
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
const filterJobs = (jobs: IndJob[]) => {
|
const { regularJobs, trackedJobs } = categorizeJobs(jobs, searchQuery);
|
||||||
if (!searchQuery) return jobs;
|
|
||||||
const query = searchQuery.toLowerCase();
|
|
||||||
return jobs.filter(job =>
|
|
||||||
job.outputItem.toLowerCase().includes(query)
|
|
||||||
);
|
|
||||||
};
|
|
||||||
|
|
||||||
const sortedJobs = [...jobs].sort((a, b) => {
|
|
||||||
const priorityA = getStatusPriority(a.status);
|
|
||||||
const priorityB = getStatusPriority(b.status);
|
|
||||||
if (priorityA === priorityB) {
|
|
||||||
return new Date(b.created || '').getTime() - new Date(a.created || '').getTime();
|
|
||||||
}
|
|
||||||
return priorityA - priorityB;
|
|
||||||
});
|
|
||||||
|
|
||||||
const regularJobs = filterJobs(sortedJobs.filter(job => job.status !== 'Tracked'));
|
|
||||||
const trackedJobs = filterJobs(sortedJobs.filter(job => job.status === 'Tracked'));
|
|
||||||
|
|
||||||
const { totalJobs, totalProfit, totalRevenue, calculateJobRevenue, calculateJobProfit } = useJobMetrics(regularJobs);
|
const { totalJobs, totalProfit, totalRevenue, calculateJobRevenue, calculateJobProfit } = useJobMetrics(regularJobs);
|
||||||
|
|
||||||
const handleCreateJob = async (jobData: IndJobRecordNoId, billOfMaterials?: { name: string; quantity: number }[]) => {
|
|
||||||
try {
|
|
||||||
const newJob = await createJob(jobData);
|
|
||||||
|
|
||||||
if (billOfMaterials && billOfMaterials.length > 0) {
|
|
||||||
const billItems = billOfMaterials.map(item => ({
|
|
||||||
name: item.name,
|
|
||||||
quantity: item.quantity,
|
|
||||||
unitPrice: 0
|
|
||||||
}));
|
|
||||||
await createMultipleBillItems(newJob.id, billItems, 'billOfMaterials');
|
|
||||||
}
|
|
||||||
|
|
||||||
setShowJobForm(false);
|
|
||||||
} catch (error) {
|
|
||||||
console.error('Error creating job:', error);
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
const handleEditJob = (job: IndJob) => {
|
|
||||||
setEditingJob(job);
|
|
||||||
setShowJobForm(true);
|
|
||||||
};
|
|
||||||
|
|
||||||
const handleUpdateJob = async (jobData: IndJobRecordNoId) => {
|
|
||||||
if (!editingJob) return;
|
|
||||||
|
|
||||||
try {
|
|
||||||
await updateJob(editingJob.id, jobData);
|
|
||||||
setShowJobForm(false);
|
|
||||||
setEditingJob(null);
|
|
||||||
} catch (error) {
|
|
||||||
console.error('Error updating job:', error);
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
const handleDeleteJob = async (jobId: string) => {
|
|
||||||
if (confirm('Are you sure you want to delete this job?')) {
|
|
||||||
try {
|
|
||||||
await deleteJob(jobId);
|
|
||||||
} catch (error) {
|
|
||||||
console.error('Error deleting job:', error);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
const handleUpdateProduced = async (jobId: string, produced: number) => {
|
|
||||||
try {
|
|
||||||
await updateJob(jobId, { produced });
|
|
||||||
} catch (error) {
|
|
||||||
console.error('Error updating produced quantity:', error);
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
const handleImportBOM = async (jobId: string, items: { name: string; quantity: number }[]) => {
|
|
||||||
try {
|
|
||||||
const billItems = items.map(item => ({
|
|
||||||
name: item.name,
|
|
||||||
quantity: item.quantity,
|
|
||||||
unitPrice: 0
|
|
||||||
}));
|
|
||||||
await createMultipleBillItems(jobId, billItems, 'billOfMaterials');
|
|
||||||
} catch (error) {
|
|
||||||
console.error('Error importing BOM:', error);
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
const toggleGroup = async (status: string) => {
|
|
||||||
const currentScrollY = window.scrollY;
|
|
||||||
|
|
||||||
const newState = { ...collapsedGroups, [status]: !collapsedGroups[status] };
|
|
||||||
setCollapsedGroups(newState);
|
|
||||||
localStorage.setItem('jobGroupsCollapsed', JSON.stringify(newState));
|
|
||||||
|
|
||||||
if (collapsedGroups[status] && !loadingStatuses.has(status)) {
|
|
||||||
await loadJobsForStatuses([status]);
|
|
||||||
|
|
||||||
setTimeout(() => {
|
|
||||||
window.scrollTo(0, currentScrollY);
|
|
||||||
}, 50);
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
const handleBatchTransactionsAssigned = async (assignments: { jobId: string, transactions: IndTransactionRecordNoId[] }[]) => {
|
|
||||||
try {
|
|
||||||
for (const { jobId, transactions } of assignments) {
|
|
||||||
await createMultipleTransactions(jobId, transactions, 'income');
|
|
||||||
}
|
|
||||||
} catch (error) {
|
|
||||||
console.error('Error assigning batch transactions:', error);
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
const handleBatchExpendituresAssigned = async (assignments: { jobId: string, transactions: IndTransactionRecordNoId[] }[]) => {
|
|
||||||
try {
|
|
||||||
for (const { jobId, transactions } of assignments) {
|
|
||||||
await createMultipleTransactions(jobId, transactions, 'expenditure');
|
|
||||||
}
|
|
||||||
} catch (error) {
|
|
||||||
console.error('Error assigning batch expenditures:', error);
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
if (showJobForm) {
|
if (showJobForm) {
|
||||||
return (
|
return (
|
||||||
<div className="min-h-screen bg-gray-950 p-6">
|
<div className="min-h-screen bg-gray-950 p-6">
|
||||||
<div className="max-w-4xl mx-auto">
|
<div className="max-w-4xl mx-auto">
|
||||||
<JobForm
|
<JobForm
|
||||||
job={editingJob || undefined}
|
job={editingJob || undefined}
|
||||||
onSubmit={editingJob ? handleUpdateJob : handleCreateJob}
|
onSubmit={editingJob ? (jobData) => handleUpdateJob(jobData, editingJob) : handleCreateJob}
|
||||||
onCancel={() => {
|
onCancel={() => {
|
||||||
setShowJobForm(false);
|
setShowJobForm(false);
|
||||||
setEditingJob(null);
|
setEditingJob(null);
|
||||||
|
|||||||
30
src/utils/jobFiltering.ts
Normal file
30
src/utils/jobFiltering.ts
Normal file
@@ -0,0 +1,30 @@
|
|||||||
|
|
||||||
|
import { IndJob } from '@/lib/types';
|
||||||
|
import { getStatusPriority } from '@/utils/jobStatusUtils';
|
||||||
|
|
||||||
|
export const filterJobs = (jobs: IndJob[], searchQuery: string) => {
|
||||||
|
if (!searchQuery) return jobs;
|
||||||
|
const query = searchQuery.toLowerCase();
|
||||||
|
return jobs.filter(job =>
|
||||||
|
job.outputItem.toLowerCase().includes(query)
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
export const sortJobs = (jobs: IndJob[]) => {
|
||||||
|
return [...jobs].sort((a, b) => {
|
||||||
|
const priorityA = getStatusPriority(a.status);
|
||||||
|
const priorityB = getStatusPriority(b.status);
|
||||||
|
if (priorityA === priorityB) {
|
||||||
|
return new Date(b.created || '').getTime() - new Date(a.created || '').getTime();
|
||||||
|
}
|
||||||
|
return priorityA - priorityB;
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
export const categorizeJobs = (jobs: IndJob[], searchQuery: string) => {
|
||||||
|
const sortedJobs = sortJobs(jobs);
|
||||||
|
const regularJobs = filterJobs(sortedJobs.filter(job => job.status !== 'Tracked'), searchQuery);
|
||||||
|
const trackedJobs = filterJobs(sortedJobs.filter(job => job.status === 'Tracked'), searchQuery);
|
||||||
|
|
||||||
|
return { regularJobs, trackedJobs };
|
||||||
|
};
|
||||||
Reference in New Issue
Block a user