Files
motovaultpro/backend/src/features/stations/data/stations.repository.ts
Eric Gullickson bb4a356b9e Google Maps Bug
2025-11-08 12:17:29 -06:00

190 lines
5.3 KiB
TypeScript

/**
* @ai-summary Data access layer for stations
*/
import { Pool } from 'pg';
import { Station, SavedStation } from '../domain/stations.types';
export class StationsRepository {
constructor(private pool: Pool) {}
async cacheStation(station: Station): Promise<void> {
const query = `
INSERT INTO station_cache (
place_id, name, address, latitude, longitude,
price_regular, price_premium, price_diesel, rating, photo_url
)
VALUES ($1, $2, $3, $4, $5, $6, $7, $8, $9, $10)
ON CONFLICT (place_id) DO UPDATE
SET name = $2, address = $3, latitude = $4, longitude = $5,
rating = $9, photo_url = $10, cached_at = NOW()
`;
await this.pool.query(query, [
station.placeId,
station.name,
station.address,
station.latitude,
station.longitude,
station.priceRegular,
station.pricePremium,
station.priceDiesel,
station.rating,
station.photoUrl
]);
}
async getCachedStation(placeId: string): Promise<Station | null> {
const query = 'SELECT * FROM station_cache WHERE place_id = $1';
const result = await this.pool.query(query, [placeId]);
if (result.rows.length === 0) {
return null;
}
return this.mapCacheRow(result.rows[0]);
}
async saveStation(
userId: string,
placeId: string,
data?: {
nickname?: string;
notes?: string;
isFavorite?: boolean;
has93Octane?: boolean;
has93OctaneEthanolFree?: boolean;
}
): Promise<SavedStation> {
const query = `
INSERT INTO saved_stations (
user_id,
place_id,
nickname,
notes,
is_favorite,
has_93_octane,
has_93_octane_ethanol_free
)
VALUES ($1, $2, $3, $4, $5, $6, $7)
ON CONFLICT (user_id, place_id) DO UPDATE
SET nickname = COALESCE($3, saved_stations.nickname),
notes = COALESCE($4, saved_stations.notes),
is_favorite = COALESCE($5, saved_stations.is_favorite),
has_93_octane = COALESCE($6, saved_stations.has_93_octane),
has_93_octane_ethanol_free = CASE
WHEN $6 IS NOT NULL AND $6 = false THEN false
ELSE COALESCE($7, saved_stations.has_93_octane_ethanol_free)
END,
updated_at = NOW()
RETURNING *
`;
const result = await this.pool.query(query, [
userId,
placeId,
data?.nickname,
data?.notes,
data?.isFavorite ?? false,
data?.has93Octane ?? false,
data?.has93OctaneEthanolFree ?? false
]);
return this.mapSavedRow(result.rows[0]);
}
async updateSavedStation(
userId: string,
placeId: string,
data: {
nickname?: string;
notes?: string;
isFavorite?: boolean;
has93Octane?: boolean;
has93OctaneEthanolFree?: boolean;
}
): Promise<SavedStation | null> {
const query = `
UPDATE saved_stations
SET
nickname = COALESCE($3, nickname),
notes = COALESCE($4, notes),
is_favorite = COALESCE($5, is_favorite),
has_93_octane = COALESCE($6, has_93_octane),
has_93_octane_ethanol_free = CASE
WHEN $6 IS NOT NULL AND $6 = false THEN false
ELSE COALESCE($7, has_93_octane_ethanol_free)
END,
updated_at = NOW()
WHERE user_id = $1 AND place_id = $2
RETURNING *
`;
const result = await this.pool.query(query, [
userId,
placeId,
data.nickname,
data.notes,
data.isFavorite,
data.has93Octane,
data.has93OctaneEthanolFree
]);
if (result.rows.length === 0) {
return null;
}
return this.mapSavedRow(result.rows[0]);
}
async getUserSavedStations(userId: string): Promise<SavedStation[]> {
const query = `
SELECT * FROM saved_stations
WHERE user_id = $1
ORDER BY is_favorite DESC, created_at DESC
`;
const result = await this.pool.query(query, [userId]);
return result.rows.map(row => this.mapSavedRow(row));
}
async deleteSavedStation(userId: string, placeId: string): Promise<boolean> {
const query = 'DELETE FROM saved_stations WHERE user_id = $1 AND place_id = $2';
const result = await this.pool.query(query, [userId, placeId]);
return (result.rowCount ?? 0) > 0;
}
private mapCacheRow(row: any): Station {
return {
id: row.id,
placeId: row.place_id,
name: row.name,
address: row.address,
latitude: parseFloat(row.latitude),
longitude: parseFloat(row.longitude),
priceRegular: row.price_regular ? parseFloat(row.price_regular) : undefined,
pricePremium: row.price_premium ? parseFloat(row.price_premium) : undefined,
priceDiesel: row.price_diesel ? parseFloat(row.price_diesel) : undefined,
rating: row.rating ? parseFloat(row.rating) : undefined,
photoUrl: row.photo_url,
lastUpdated: row.cached_at
};
}
private mapSavedRow(row: any): SavedStation {
return {
id: row.id,
userId: row.user_id,
stationId: row.place_id,
placeId: row.place_id,
nickname: row.nickname,
notes: row.notes,
isFavorite: row.is_favorite,
has93Octane: row.has_93_octane ?? false,
has93OctaneEthanolFree: row.has_93_octane_ethanol_free ?? false,
createdAt: row.created_at,
updatedAt: row.updated_at
};
}
}