Add src/lib/database/carDatabase.ts
This commit is contained in:
333
src/lib/database/carDatabase.ts
Normal file
333
src/lib/database/carDatabase.ts
Normal file
@@ -0,0 +1,333 @@
|
||||
/**
|
||||
* 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();
|
||||
Reference in New Issue
Block a user