Refactor: Centralize database operations
Consolidated database interaction logic into service files. Streamlined job and transaction creation/update processes to minimize database requests, improving efficiency.
This commit is contained in:
261
src/services/dataService.ts
Normal file
261
src/services/dataService.ts
Normal file
@@ -0,0 +1,261 @@
|
||||
|
||||
import { IndJob } from '@/lib/types';
|
||||
import { IndJobRecord, IndJobRecordNoId, IndTransactionRecord, IndTransactionRecordNoId, IndBillitemRecord, IndBillitemRecordNoId } from '@/lib/pbtypes';
|
||||
import * as jobService from './jobService';
|
||||
import * as transactionService from './transactionService';
|
||||
import * as billItemService from './billItemService';
|
||||
|
||||
export class DataService {
|
||||
private static instance: DataService;
|
||||
private jobs: IndJob[] = [];
|
||||
private listeners: Set<() => void> = new Set();
|
||||
|
||||
private constructor() {}
|
||||
|
||||
static getInstance(): DataService {
|
||||
if (!DataService.instance) {
|
||||
DataService.instance = new DataService();
|
||||
}
|
||||
return DataService.instance;
|
||||
}
|
||||
|
||||
subscribe(listener: () => void) {
|
||||
this.listeners.add(listener);
|
||||
return () => this.listeners.delete(listener);
|
||||
}
|
||||
|
||||
private notifyListeners() {
|
||||
this.listeners.forEach(listener => listener());
|
||||
}
|
||||
|
||||
getJobs(): IndJob[] {
|
||||
return [...this.jobs];
|
||||
}
|
||||
|
||||
getJob(id: string): IndJob | null {
|
||||
return this.jobs.find(job => job.id === id) || null;
|
||||
}
|
||||
|
||||
async loadJobs(): Promise<IndJob[]> {
|
||||
console.log('Loading jobs from database');
|
||||
this.jobs = await jobService.getJobsFull();
|
||||
this.notifyListeners();
|
||||
return this.getJobs();
|
||||
}
|
||||
|
||||
async createJob(jobData: IndJobRecordNoId): Promise<IndJob> {
|
||||
console.log('Creating job:', jobData);
|
||||
const newJobRecord = await jobService.createJob(jobData);
|
||||
const newJob: IndJob = {
|
||||
...newJobRecord,
|
||||
expenditures: [],
|
||||
income: [],
|
||||
billOfMaterials: [],
|
||||
consumedMaterials: []
|
||||
};
|
||||
|
||||
this.jobs.push(newJob);
|
||||
this.notifyListeners();
|
||||
return newJob;
|
||||
}
|
||||
|
||||
async updateJob(id: string, updates: Partial<IndJobRecord>): Promise<IndJob> {
|
||||
console.log('Updating job:', id, updates);
|
||||
const updatedRecord = await jobService.updateJob(id, updates);
|
||||
|
||||
const jobIndex = this.jobs.findIndex(job => job.id === id);
|
||||
if (jobIndex !== -1) {
|
||||
// Preserve existing relations while updating the record fields
|
||||
this.jobs[jobIndex] = {
|
||||
...this.jobs[jobIndex],
|
||||
...updatedRecord
|
||||
};
|
||||
this.notifyListeners();
|
||||
return this.jobs[jobIndex];
|
||||
}
|
||||
|
||||
throw new Error(`Job with id ${id} not found in local state`);
|
||||
}
|
||||
|
||||
async deleteJob(id: string): Promise<void> {
|
||||
console.log('Deleting job:', id);
|
||||
await jobService.deleteJob(id);
|
||||
|
||||
this.jobs = this.jobs.filter(job => job.id !== id);
|
||||
this.notifyListeners();
|
||||
}
|
||||
|
||||
async createTransaction(jobId: string, transaction: IndTransactionRecordNoId, type: 'expenditure' | 'income'): Promise<IndJob> {
|
||||
console.log('Creating transaction for job:', jobId, transaction, type);
|
||||
|
||||
const job = this.getJob(jobId);
|
||||
if (!job) throw new Error(`Job with id ${jobId} not found`);
|
||||
|
||||
// Create the transaction in the database
|
||||
transaction.job = jobId;
|
||||
const createdTransaction = await transactionService.createTransaction(job, transaction, type);
|
||||
|
||||
// Update the job's transaction references in the database
|
||||
const field = type === 'expenditure' ? 'expenditures' : 'income';
|
||||
const currentIds = (job[field] || []).map(tr => tr.id);
|
||||
await jobService.updateJob(jobId, {
|
||||
[field]: [...currentIds, createdTransaction.id]
|
||||
});
|
||||
|
||||
// Update local state without re-fetching
|
||||
const jobIndex = this.jobs.findIndex(j => j.id === jobId);
|
||||
if (jobIndex !== -1) {
|
||||
if (type === 'expenditure') {
|
||||
this.jobs[jobIndex].expenditures.push(createdTransaction);
|
||||
} else {
|
||||
this.jobs[jobIndex].income.push(createdTransaction);
|
||||
}
|
||||
this.notifyListeners();
|
||||
return this.jobs[jobIndex];
|
||||
}
|
||||
|
||||
throw new Error(`Job with id ${jobId} not found in local state`);
|
||||
}
|
||||
|
||||
async createMultipleTransactions(jobId: string, transactions: IndTransactionRecordNoId[], type: 'expenditure' | 'income'): Promise<IndJob> {
|
||||
console.log('Creating multiple transactions for job:', jobId, transactions.length, type);
|
||||
|
||||
const job = this.getJob(jobId);
|
||||
if (!job) throw new Error(`Job with id ${jobId} not found`);
|
||||
|
||||
const createdTransactions: IndTransactionRecord[] = [];
|
||||
|
||||
// Create all transactions
|
||||
for (const transaction of transactions) {
|
||||
transaction.job = jobId;
|
||||
const createdTransaction = await transactionService.createTransaction(job, transaction, type);
|
||||
createdTransactions.push(createdTransaction);
|
||||
}
|
||||
|
||||
// Update the job's transaction references in one database call
|
||||
const field = type === 'expenditure' ? 'expenditures' : 'income';
|
||||
const currentIds = (job[field] || []).map(tr => tr.id);
|
||||
const newIds = createdTransactions.map(tr => tr.id);
|
||||
await jobService.updateJob(jobId, {
|
||||
[field]: [...currentIds, ...newIds]
|
||||
});
|
||||
|
||||
// Update local state
|
||||
const jobIndex = this.jobs.findIndex(j => j.id === jobId);
|
||||
if (jobIndex !== -1) {
|
||||
if (type === 'expenditure') {
|
||||
this.jobs[jobIndex].expenditures.push(...createdTransactions);
|
||||
} else {
|
||||
this.jobs[jobIndex].income.push(...createdTransactions);
|
||||
}
|
||||
this.notifyListeners();
|
||||
return this.jobs[jobIndex];
|
||||
}
|
||||
|
||||
throw new Error(`Job with id ${jobId} not found in local state`);
|
||||
}
|
||||
|
||||
async updateTransaction(jobId: string, transactionId: string, updates: Partial<IndTransactionRecord>): Promise<IndJob> {
|
||||
console.log('Updating transaction:', transactionId, updates);
|
||||
|
||||
const job = this.getJob(jobId);
|
||||
if (!job) throw new Error(`Job with id ${jobId} not found`);
|
||||
|
||||
const updatedTransaction = await transactionService.updateTransaction(job, transactionId, updates);
|
||||
|
||||
// Update local state
|
||||
const jobIndex = this.jobs.findIndex(j => j.id === jobId);
|
||||
if (jobIndex !== -1) {
|
||||
this.jobs[jobIndex].expenditures = this.jobs[jobIndex].expenditures.map(tx =>
|
||||
tx.id === transactionId ? updatedTransaction : tx
|
||||
);
|
||||
this.jobs[jobIndex].income = this.jobs[jobIndex].income.map(tx =>
|
||||
tx.id === transactionId ? updatedTransaction : tx
|
||||
);
|
||||
this.notifyListeners();
|
||||
return this.jobs[jobIndex];
|
||||
}
|
||||
|
||||
throw new Error(`Job with id ${jobId} not found in local state`);
|
||||
}
|
||||
|
||||
async deleteTransaction(jobId: string, transactionId: string): Promise<IndJob> {
|
||||
console.log('Deleting transaction:', transactionId);
|
||||
|
||||
const job = this.getJob(jobId);
|
||||
if (!job) throw new Error(`Job with id ${jobId} not found`);
|
||||
|
||||
await transactionService.deleteTransaction(job, transactionId);
|
||||
|
||||
// Update local state
|
||||
const jobIndex = this.jobs.findIndex(j => j.id === jobId);
|
||||
if (jobIndex !== -1) {
|
||||
this.jobs[jobIndex].expenditures = this.jobs[jobIndex].expenditures.filter(tx => tx.id !== transactionId);
|
||||
this.jobs[jobIndex].income = this.jobs[jobIndex].income.filter(tx => tx.id !== transactionId);
|
||||
this.notifyListeners();
|
||||
return this.jobs[jobIndex];
|
||||
}
|
||||
|
||||
throw new Error(`Job with id ${jobId} not found in local state`);
|
||||
}
|
||||
|
||||
async createBillItem(jobId: string, billItem: IndBillitemRecordNoId, type: 'billOfMaterials' | 'consumedMaterials'): Promise<IndJob> {
|
||||
console.log('Creating bill item for job:', jobId, billItem, type);
|
||||
|
||||
const job = this.getJob(jobId);
|
||||
if (!job) throw new Error(`Job with id ${jobId} not found`);
|
||||
|
||||
const createdBillItem = await billItemService.addBillItem(jobId, billItem);
|
||||
|
||||
// Update the job's bill item references
|
||||
const currentIds = (job[type] || []).map(item => item.id);
|
||||
await jobService.updateJob(jobId, {
|
||||
[type]: [...currentIds, createdBillItem.id]
|
||||
});
|
||||
|
||||
// Update local state
|
||||
const jobIndex = this.jobs.findIndex(j => j.id === jobId);
|
||||
if (jobIndex !== -1) {
|
||||
this.jobs[jobIndex][type].push(createdBillItem);
|
||||
this.notifyListeners();
|
||||
return this.jobs[jobIndex];
|
||||
}
|
||||
|
||||
throw new Error(`Job with id ${jobId} not found in local state`);
|
||||
}
|
||||
|
||||
async createMultipleBillItems(jobId: string, billItems: IndBillitemRecordNoId[], type: 'billOfMaterials' | 'consumedMaterials'): Promise<IndJob> {
|
||||
console.log('Creating multiple bill items for job:', jobId, billItems.length, type);
|
||||
|
||||
const job = this.getJob(jobId);
|
||||
if (!job) throw new Error(`Job with id ${jobId} not found`);
|
||||
|
||||
const createdBillItems: IndBillitemRecord[] = [];
|
||||
|
||||
// Create all bill items
|
||||
for (const billItem of billItems) {
|
||||
const createdBillItem = await billItemService.addBillItem(jobId, billItem);
|
||||
createdBillItems.push(createdBillItem);
|
||||
}
|
||||
|
||||
// Update the job's bill item references in one database call
|
||||
const currentIds = (job[type] || []).map(item => item.id);
|
||||
const newIds = createdBillItems.map(item => item.id);
|
||||
await jobService.updateJob(jobId, {
|
||||
[type]: [...currentIds, ...newIds]
|
||||
});
|
||||
|
||||
// Update local state
|
||||
const jobIndex = this.jobs.findIndex(j => j.id === jobId);
|
||||
if (jobIndex !== -1) {
|
||||
this.jobs[jobIndex][type].push(...createdBillItems);
|
||||
this.notifyListeners();
|
||||
return this.jobs[jobIndex];
|
||||
}
|
||||
|
||||
throw new Error(`Job with id ${jobId} not found in local state`);
|
||||
}
|
||||
}
|
||||
|
||||
// Export singleton instance
|
||||
export const dataService = DataService.getInstance();
|
||||
Reference in New Issue
Block a user