334 lines
7.9 KiB
TypeScript
334 lines
7.9 KiB
TypeScript
/**
|
|
* Car Database Service
|
|
* Handles all car data operations and queries
|
|
*/
|
|
|
|
import { Car, CarFilter, CarComparison, CarInventory } from './carTypes';
|
|
|
|
export class CarDatabase {
|
|
private cars: Car[] = [];
|
|
|
|
/**
|
|
* Initialize database with sample data
|
|
*/
|
|
initialize(cars: Car[]): void {
|
|
this.cars = cars;
|
|
}
|
|
|
|
/**
|
|
* Get all cars
|
|
*/
|
|
getAllCars(): Car[] {
|
|
return this.cars;
|
|
}
|
|
|
|
/**
|
|
* Get car by ID
|
|
*/
|
|
getCarById(id: string): Car | undefined {
|
|
return this.cars.find((car) => car.id === id);
|
|
}
|
|
|
|
/**
|
|
* Search cars by filter criteria
|
|
*/
|
|
filterCars(filter: CarFilter): Car[] {
|
|
return this.cars.filter((car) => {
|
|
// Make filter
|
|
if (filter.make && !filter.make.includes(car.make)) {
|
|
return false;
|
|
}
|
|
|
|
// Model filter
|
|
if (filter.model && !filter.model.includes(car.model)) {
|
|
return false;
|
|
}
|
|
|
|
// Year range filter
|
|
if (filter.yearRange) {
|
|
const [minYear, maxYear] = filter.yearRange;
|
|
if (car.year < minYear || car.year > maxYear) {
|
|
return false;
|
|
}
|
|
}
|
|
|
|
// Price range filter
|
|
if (filter.priceRange) {
|
|
const [minPrice, maxPrice] = filter.priceRange;
|
|
if (car.price < minPrice || car.price > maxPrice) {
|
|
return false;
|
|
}
|
|
}
|
|
|
|
// Transmission filter
|
|
if (filter.transmission && !filter.transmission.includes(car.transmission)) {
|
|
return false;
|
|
}
|
|
|
|
// Fuel filter
|
|
if (filter.fuel && !filter.fuel.includes(car.fuel)) {
|
|
return false;
|
|
}
|
|
|
|
// Body type filter
|
|
if (filter.bodyType && !filter.bodyType.includes(car.bodyType || '')) {
|
|
return false;
|
|
}
|
|
|
|
// Drive type filter
|
|
if (filter.driveType && !filter.driveType.includes(car.driveType || '')) {
|
|
return false;
|
|
}
|
|
|
|
// Seating filter
|
|
if (filter.seatCount && car.seating) {
|
|
const [minSeats, maxSeats] = filter.seatCount;
|
|
if (car.seating < minSeats || car.seating > maxSeats) {
|
|
return false;
|
|
}
|
|
}
|
|
|
|
// Safety rating filter
|
|
if (filter.safetyRating && car.safetyRating && car.safetyRating < filter.safetyRating) {
|
|
return false;
|
|
}
|
|
|
|
// Keyword search
|
|
if (filter.keyword) {
|
|
const keyword = filter.keyword.toLowerCase();
|
|
const searchableFields = [
|
|
car.make,
|
|
car.model,
|
|
car.bodyType,
|
|
car.color,
|
|
car.description,
|
|
].filter(Boolean);
|
|
|
|
const matches = searchableFields.some(
|
|
(field) => field && field.toString().toLowerCase().includes(keyword)
|
|
);
|
|
|
|
if (!matches) {
|
|
return false;
|
|
}
|
|
}
|
|
|
|
return true;
|
|
});
|
|
}
|
|
|
|
/**
|
|
* Search cars by keyword
|
|
*/
|
|
searchCars(keyword: string): Car[] {
|
|
return this.filterCars({ keyword });
|
|
}
|
|
|
|
/**
|
|
* Get cars by make
|
|
*/
|
|
getCarsByMake(make: string): Car[] {
|
|
return this.cars.filter((car) => car.make.toLowerCase() === make.toLowerCase());
|
|
}
|
|
|
|
/**
|
|
* Get cars by fuel type
|
|
*/
|
|
getCarsByFuel(fuel: string): Car[] {
|
|
return this.cars.filter((car) => car.fuel === fuel);
|
|
}
|
|
|
|
/**
|
|
* Get cars by body type
|
|
*/
|
|
getCarsByBodyType(bodyType: string): Car[] {
|
|
return this.cars.filter((car) => car.bodyType === bodyType);
|
|
}
|
|
|
|
/**
|
|
* Get cars within price range
|
|
*/
|
|
getCarsByPriceRange(minPrice: number, maxPrice: number): Car[] {
|
|
return this.cars.filter((car) => car.price >= minPrice && car.price <= maxPrice);
|
|
}
|
|
|
|
/**
|
|
* Sort cars by field
|
|
*/
|
|
sortCars(cars: Car[], sortBy: keyof Car, ascending: boolean = true): Car[] {
|
|
const sorted = [...cars].sort((a, b) => {
|
|
const aValue = a[sortBy];
|
|
const bValue = b[sortBy];
|
|
|
|
if (typeof aValue === 'number' && typeof bValue === 'number') {
|
|
return ascending ? aValue - bValue : bValue - aValue;
|
|
}
|
|
|
|
if (typeof aValue === 'string' && typeof bValue === 'string') {
|
|
return ascending ? aValue.localeCompare(bValue) : bValue.localeCompare(aValue);
|
|
}
|
|
|
|
return 0;
|
|
});
|
|
|
|
return sorted;
|
|
}
|
|
|
|
/**
|
|
* Get unique makes
|
|
*/
|
|
getUniqueMakes(): string[] {
|
|
const makes = new Set(this.cars.map((car) => car.make));
|
|
return Array.from(makes).sort();
|
|
}
|
|
|
|
/**
|
|
* Get unique models for a make
|
|
*/
|
|
getUniqueModels(make: string): string[] {
|
|
const models = new Set(
|
|
this.cars.filter((car) => car.make === make).map((car) => car.model)
|
|
);
|
|
return Array.from(models).sort();
|
|
}
|
|
|
|
/**
|
|
* Get unique fuel types
|
|
*/
|
|
getUniqueFuelTypes(): string[] {
|
|
const fuels = new Set(this.cars.map((car) => car.fuel));
|
|
return Array.from(fuels).sort();
|
|
}
|
|
|
|
/**
|
|
* Get unique body types
|
|
*/
|
|
getUniqueBodyTypes(): string[] {
|
|
const bodyTypes = new Set(this.cars.filter((car) => car.bodyType).map((car) => car.bodyType));
|
|
return Array.from(bodyTypes).sort();
|
|
}
|
|
|
|
/**
|
|
* Compare two or more cars
|
|
*/
|
|
compareCars(carIds: string[]): Car[] {
|
|
return carIds
|
|
.map((id) => this.getCarById(id))
|
|
.filter((car) => car !== undefined) as Car[];
|
|
}
|
|
|
|
/**
|
|
* Get similar cars based on specifications
|
|
*/
|
|
getSimilarCars(carId: string, count: number = 3): Car[] {
|
|
const car = this.getCarById(carId);
|
|
if (!car) return [];
|
|
|
|
const similar = this.cars
|
|
.filter((c) => c.id !== carId && c.make === car.make)
|
|
.slice(0, count);
|
|
|
|
return similar.length >= count
|
|
? similar
|
|
: this.cars.filter((c) => c.id !== carId && c.bodyType === car.bodyType).slice(0, count);
|
|
}
|
|
|
|
/**
|
|
* Get top rated cars
|
|
*/
|
|
getTopRatedCars(count: number = 5): Car[] {
|
|
return [...this.cars]
|
|
.sort((a, b) => (b.userRating || 0) - (a.userRating || 0))
|
|
.slice(0, count);
|
|
}
|
|
|
|
/**
|
|
* Get most viewed cars
|
|
*/
|
|
getMostViewedCars(count: number = 5): Car[] {
|
|
return [...this.cars]
|
|
.sort((a, b) => (b.viewCount || 0) - (a.viewCount || 0))
|
|
.slice(0, count);
|
|
}
|
|
|
|
/**
|
|
* Get featured cars
|
|
*/
|
|
getFeaturedCars(count: number = 5): Car[] {
|
|
return this.cars.filter((car) => car.isFavorite).slice(0, count);
|
|
}
|
|
|
|
/**
|
|
* Calculate average specifications for a make or body type
|
|
*/
|
|
calculateAverageSpecs(
|
|
cars: Car[]
|
|
): Partial<Car> {
|
|
if (cars.length === 0) return {};
|
|
|
|
const specs: Partial<Car> = {
|
|
price:
|
|
cars.reduce((sum, car) => sum + car.price, 0) / cars.length,
|
|
horsepower:
|
|
cars.reduce((sum, car) => sum + (car.horsepower || 0), 0) / cars.length,
|
|
mpg: cars.reduce((sum, car) => sum + (car.mpg || 0), 0) / cars.length,
|
|
seating:
|
|
Math.round(cars.reduce((sum, car) => sum + (car.seating || 0), 0) / cars.length),
|
|
};
|
|
|
|
return specs;
|
|
}
|
|
|
|
/**
|
|
* Add a new car to database
|
|
*/
|
|
addCar(car: Car): void {
|
|
this.cars.push(car);
|
|
}
|
|
|
|
/**
|
|
* Update a car
|
|
*/
|
|
updateCar(id: string, updates: Partial<Car>): Car | undefined {
|
|
const index = this.cars.findIndex((car) => car.id === id);
|
|
if (index !== -1) {
|
|
this.cars[index] = { ...this.cars[index], ...updates, updatedAt: new Date() };
|
|
return this.cars[index];
|
|
}
|
|
return undefined;
|
|
}
|
|
|
|
/**
|
|
* Delete a car
|
|
*/
|
|
deleteCar(id: string): boolean {
|
|
const index = this.cars.findIndex((car) => car.id === id);
|
|
if (index !== -1) {
|
|
this.cars.splice(index, 1);
|
|
return true;
|
|
}
|
|
return false;
|
|
}
|
|
|
|
/**
|
|
* Get database statistics
|
|
*/
|
|
getStatistics() {
|
|
return {
|
|
totalCars: this.cars.length,
|
|
averagePrice: this.cars.reduce((sum, car) => sum + car.price, 0) / this.cars.length,
|
|
makes: this.getUniqueMakes().length,
|
|
models: this.cars.length,
|
|
fuelTypes: this.getUniqueFuelTypes().length,
|
|
bodyTypes: this.getUniqueBodyTypes().length,
|
|
averageHorsepower:
|
|
this.cars.reduce((sum, car) => sum + (car.horsepower || 0), 0) / this.cars.length,
|
|
averageMpg:
|
|
this.cars.reduce((sum, car) => sum + (car.mpg || 0), 0) / this.cars.length,
|
|
};
|
|
}
|
|
}
|
|
|
|
// Export singleton instance
|
|
export const carDatabase = new CarDatabase();
|