Refactor storage architecture with modular IndexedDB services
Implements a more flexible, modular approach to IndexedDB storage: - Introduces IStore interface for dynamic store creation - Adds specialized services for each entity (BookingService, CustomerService, etc.) - Moves serialization logic to entity-specific classes - Improves dependency injection and extensibility Enables easier addition of new entity types with minimal configuration
This commit is contained in:
parent
88cccb3456
commit
2aa9d06fab
13 changed files with 1048 additions and 236 deletions
144
src/storage/customers/CustomerService.ts
Normal file
144
src/storage/customers/CustomerService.ts
Normal file
|
|
@ -0,0 +1,144 @@
|
|||
import { ICustomer } from '../../types/CustomerTypes';
|
||||
import { CustomerStore } from './CustomerStore';
|
||||
|
||||
/**
|
||||
* CustomerService - CRUD operations for customers in IndexedDB
|
||||
*
|
||||
* Handles all customer-related database operations.
|
||||
* Part of modular storage architecture where each entity has its own service.
|
||||
*
|
||||
* Note: No serialization needed - ICustomer has no Date fields.
|
||||
*/
|
||||
export class CustomerService {
|
||||
private db: IDBDatabase;
|
||||
|
||||
/**
|
||||
* @param db - IDBDatabase instance (injected dependency)
|
||||
*/
|
||||
constructor(db: IDBDatabase) {
|
||||
this.db = db;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get a single customer by ID
|
||||
*
|
||||
* @param id - Customer ID
|
||||
* @returns ICustomer or null if not found
|
||||
*/
|
||||
async get(id: string): Promise<ICustomer | null> {
|
||||
return new Promise((resolve, reject) => {
|
||||
const transaction = this.db.transaction([CustomerStore.STORE_NAME], 'readonly');
|
||||
const store = transaction.objectStore(CustomerStore.STORE_NAME);
|
||||
const request = store.get(id);
|
||||
|
||||
request.onsuccess = () => {
|
||||
resolve(request.result || null);
|
||||
};
|
||||
|
||||
request.onerror = () => {
|
||||
reject(new Error(`Failed to get customer ${id}: ${request.error}`));
|
||||
};
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Get all customers
|
||||
*
|
||||
* @returns Array of all customers
|
||||
*/
|
||||
async getAll(): Promise<ICustomer[]> {
|
||||
return new Promise((resolve, reject) => {
|
||||
const transaction = this.db.transaction([CustomerStore.STORE_NAME], 'readonly');
|
||||
const store = transaction.objectStore(CustomerStore.STORE_NAME);
|
||||
const request = store.getAll();
|
||||
|
||||
request.onsuccess = () => {
|
||||
resolve(request.result as ICustomer[]);
|
||||
};
|
||||
|
||||
request.onerror = () => {
|
||||
reject(new Error(`Failed to get all customers: ${request.error}`));
|
||||
};
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Save a customer (create or update)
|
||||
*
|
||||
* @param customer - ICustomer to save
|
||||
*/
|
||||
async save(customer: ICustomer): Promise<void> {
|
||||
return new Promise((resolve, reject) => {
|
||||
const transaction = this.db.transaction([CustomerStore.STORE_NAME], 'readwrite');
|
||||
const store = transaction.objectStore(CustomerStore.STORE_NAME);
|
||||
const request = store.put(customer);
|
||||
|
||||
request.onsuccess = () => {
|
||||
resolve();
|
||||
};
|
||||
|
||||
request.onerror = () => {
|
||||
reject(new Error(`Failed to save customer ${customer.id}: ${request.error}`));
|
||||
};
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Delete a customer
|
||||
*
|
||||
* @param id - Customer ID to delete
|
||||
*/
|
||||
async delete(id: string): Promise<void> {
|
||||
return new Promise((resolve, reject) => {
|
||||
const transaction = this.db.transaction([CustomerStore.STORE_NAME], 'readwrite');
|
||||
const store = transaction.objectStore(CustomerStore.STORE_NAME);
|
||||
const request = store.delete(id);
|
||||
|
||||
request.onsuccess = () => {
|
||||
resolve();
|
||||
};
|
||||
|
||||
request.onerror = () => {
|
||||
reject(new Error(`Failed to delete customer ${id}: ${request.error}`));
|
||||
};
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Get customers by phone number
|
||||
*
|
||||
* @param phone - Phone number
|
||||
* @returns Array of customers with this phone
|
||||
*/
|
||||
async getByPhone(phone: string): Promise<ICustomer[]> {
|
||||
return new Promise((resolve, reject) => {
|
||||
const transaction = this.db.transaction([CustomerStore.STORE_NAME], 'readonly');
|
||||
const store = transaction.objectStore(CustomerStore.STORE_NAME);
|
||||
const index = store.index('phone');
|
||||
const request = index.getAll(phone);
|
||||
|
||||
request.onsuccess = () => {
|
||||
resolve(request.result as ICustomer[]);
|
||||
};
|
||||
|
||||
request.onerror = () => {
|
||||
reject(new Error(`Failed to get customers by phone ${phone}: ${request.error}`));
|
||||
};
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Search customers by name (partial match)
|
||||
*
|
||||
* @param searchTerm - Search term (case insensitive)
|
||||
* @returns Array of customers matching search
|
||||
*/
|
||||
async searchByName(searchTerm: string): Promise<ICustomer[]> {
|
||||
const allCustomers = await this.getAll();
|
||||
const lowerSearch = searchTerm.toLowerCase();
|
||||
|
||||
return allCustomers.filter(customer =>
|
||||
customer.name.toLowerCase().includes(lowerSearch)
|
||||
);
|
||||
}
|
||||
}
|
||||
Loading…
Add table
Add a link
Reference in a new issue