Fix Admin Interface

This commit is contained in:
Eric Gullickson
2025-11-06 20:36:31 -06:00
parent 5630979adf
commit d30c2bad8f
6 changed files with 220 additions and 57 deletions

View File

@@ -10,24 +10,32 @@ import { PlatformCacheService } from '../../platform/domain/platform-cache.servi
export interface CatalogMake {
id: number;
name: string;
createdAt: string;
updatedAt: string;
}
export interface CatalogModel {
id: number;
makeId: number;
name: string;
createdAt: string;
updatedAt: string;
}
export interface CatalogYear {
id: number;
modelId: number;
year: number;
createdAt: string;
updatedAt: string;
}
export interface CatalogTrim {
id: number;
yearId: number;
name: string;
createdAt: string;
updatedAt: string;
}
export interface CatalogEngine {
@@ -35,6 +43,8 @@ export interface CatalogEngine {
trimId: number;
name: string;
description?: string;
createdAt: string;
updatedAt: string;
}
export interface PlatformChangeLog {
@@ -58,7 +68,7 @@ export class VehicleCatalogService {
async getAllMakes(): Promise<CatalogMake[]> {
const query = `
SELECT cache_key, data
SELECT cache_key, data, created_at, updated_at
FROM vehicle_dropdown_cache
WHERE cache_key LIKE 'catalog:makes:%'
ORDER BY (data->>'name')
@@ -66,10 +76,18 @@ export class VehicleCatalogService {
try {
const result = await this.pool.query(query);
return result.rows.map(row => ({
id: parseInt(row.cache_key.split(':')[2]),
name: row.data.name
}));
return result.rows.map(row => {
const createdAt =
row.data?.createdAt ?? this.toIsoDate(row.created_at);
const updatedAt =
row.data?.updatedAt ?? this.toIsoDate(row.updated_at);
return {
id: parseInt(row.cache_key.split(':')[2]),
name: row.data.name,
createdAt,
updatedAt,
};
});
} catch (error) {
logger.error('Error getting all makes', { error });
throw error;
@@ -91,7 +109,8 @@ export class VehicleCatalogService {
const makeId = idResult.rows[0].next_id;
// Insert make
const make: CatalogMake = { id: makeId, name };
const now = new Date().toISOString();
const make: CatalogMake = { id: makeId, name, createdAt: now, updatedAt: now };
await client.query(`
INSERT INTO vehicle_dropdown_cache (cache_key, data, expires_at)
VALUES ($1, $2, NOW() + INTERVAL '10 years')
@@ -124,15 +143,23 @@ export class VehicleCatalogService {
// Get old value
const oldResult = await client.query(`
SELECT data FROM vehicle_dropdown_cache WHERE cache_key = $1
SELECT data, created_at FROM vehicle_dropdown_cache WHERE cache_key = $1
`, [`catalog:makes:${makeId}`]);
if (oldResult.rows.length === 0) {
throw new Error(`Make ${makeId} not found`);
}
const oldValue = oldResult.rows[0].data;
const newValue: CatalogMake = { id: makeId, name };
const oldRow = oldResult.rows[0];
const oldValue = oldRow.data;
const createdAt =
oldValue?.createdAt ?? this.toIsoDate(oldRow.created_at);
const newValue: CatalogMake = {
id: makeId,
name,
createdAt,
updatedAt: new Date().toISOString(),
};
// Update make
await client.query(`
@@ -216,7 +243,7 @@ export class VehicleCatalogService {
async getModelsByMake(makeId: number): Promise<CatalogModel[]> {
const query = `
SELECT cache_key, data
SELECT cache_key, data, created_at, updated_at
FROM vehicle_dropdown_cache
WHERE cache_key LIKE 'catalog:models:%'
AND (data->>'makeId')::int = $1
@@ -228,7 +255,11 @@ export class VehicleCatalogService {
return result.rows.map(row => ({
id: parseInt(row.cache_key.split(':')[2]),
makeId: row.data.makeId,
name: row.data.name
name: row.data.name,
createdAt:
row.data?.createdAt ?? this.toIsoDate(row.created_at),
updatedAt:
row.data?.updatedAt ?? this.toIsoDate(row.updated_at),
}));
} catch (error) {
logger.error('Error getting models by make', { error, makeId });
@@ -260,7 +291,14 @@ export class VehicleCatalogService {
const modelId = idResult.rows[0].next_id;
// Insert model
const model: CatalogModel = { id: modelId, makeId, name };
const now = new Date().toISOString();
const model: CatalogModel = {
id: modelId,
makeId,
name,
createdAt: now,
updatedAt: now,
};
await client.query(`
INSERT INTO vehicle_dropdown_cache (cache_key, data, expires_at)
VALUES ($1, $2, NOW() + INTERVAL '10 years')
@@ -302,15 +340,24 @@ export class VehicleCatalogService {
// Get old value
const oldResult = await client.query(`
SELECT data FROM vehicle_dropdown_cache WHERE cache_key = $1
SELECT data, created_at FROM vehicle_dropdown_cache WHERE cache_key = $1
`, [`catalog:models:${modelId}`]);
if (oldResult.rows.length === 0) {
throw new Error(`Model ${modelId} not found`);
}
const oldValue = oldResult.rows[0].data;
const newValue: CatalogModel = { id: modelId, makeId, name };
const oldRow = oldResult.rows[0];
const oldValue = oldRow.data;
const createdAt =
oldValue?.createdAt ?? this.toIsoDate(oldRow.created_at);
const newValue: CatalogModel = {
id: modelId,
makeId,
name,
createdAt,
updatedAt: new Date().toISOString(),
};
// Update model
await client.query(`
@@ -394,7 +441,7 @@ export class VehicleCatalogService {
async getYearsByModel(modelId: number): Promise<CatalogYear[]> {
const query = `
SELECT cache_key, data
SELECT cache_key, data, created_at, updated_at
FROM vehicle_dropdown_cache
WHERE cache_key LIKE 'catalog:years:%'
AND (data->>'modelId')::int = $1
@@ -406,7 +453,11 @@ export class VehicleCatalogService {
return result.rows.map(row => ({
id: parseInt(row.cache_key.split(':')[2]),
modelId: row.data.modelId,
year: row.data.year
year: row.data.year,
createdAt:
row.data?.createdAt ?? this.toIsoDate(row.created_at),
updatedAt:
row.data?.updatedAt ?? this.toIsoDate(row.updated_at),
}));
} catch (error) {
logger.error('Error getting years by model', { error, modelId });
@@ -438,7 +489,14 @@ export class VehicleCatalogService {
const yearId = idResult.rows[0].next_id;
// Insert year
const yearData: CatalogYear = { id: yearId, modelId, year };
const now = new Date().toISOString();
const yearData: CatalogYear = {
id: yearId,
modelId,
year,
createdAt: now,
updatedAt: now,
};
await client.query(`
INSERT INTO vehicle_dropdown_cache (cache_key, data, expires_at)
VALUES ($1, $2, NOW() + INTERVAL '10 years')
@@ -480,15 +538,24 @@ export class VehicleCatalogService {
// Get old value
const oldResult = await client.query(`
SELECT data FROM vehicle_dropdown_cache WHERE cache_key = $1
SELECT data, created_at FROM vehicle_dropdown_cache WHERE cache_key = $1
`, [`catalog:years:${yearId}`]);
if (oldResult.rows.length === 0) {
throw new Error(`Year ${yearId} not found`);
}
const oldValue = oldResult.rows[0].data;
const newValue: CatalogYear = { id: yearId, modelId, year };
const oldRow = oldResult.rows[0];
const oldValue = oldRow.data;
const createdAt =
oldValue?.createdAt ?? this.toIsoDate(oldRow.created_at);
const newValue: CatalogYear = {
id: yearId,
modelId,
year,
createdAt,
updatedAt: new Date().toISOString(),
};
// Update year
await client.query(`
@@ -572,7 +639,7 @@ export class VehicleCatalogService {
async getTrimsByYear(yearId: number): Promise<CatalogTrim[]> {
const query = `
SELECT cache_key, data
SELECT cache_key, data, created_at, updated_at
FROM vehicle_dropdown_cache
WHERE cache_key LIKE 'catalog:trims:%'
AND (data->>'yearId')::int = $1
@@ -584,7 +651,11 @@ export class VehicleCatalogService {
return result.rows.map(row => ({
id: parseInt(row.cache_key.split(':')[2]),
yearId: row.data.yearId,
name: row.data.name
name: row.data.name,
createdAt:
row.data?.createdAt ?? this.toIsoDate(row.created_at),
updatedAt:
row.data?.updatedAt ?? this.toIsoDate(row.updated_at),
}));
} catch (error) {
logger.error('Error getting trims by year', { error, yearId });
@@ -616,7 +687,14 @@ export class VehicleCatalogService {
const trimId = idResult.rows[0].next_id;
// Insert trim
const trim: CatalogTrim = { id: trimId, yearId, name };
const now = new Date().toISOString();
const trim: CatalogTrim = {
id: trimId,
yearId,
name,
createdAt: now,
updatedAt: now,
};
await client.query(`
INSERT INTO vehicle_dropdown_cache (cache_key, data, expires_at)
VALUES ($1, $2, NOW() + INTERVAL '10 years')
@@ -658,15 +736,24 @@ export class VehicleCatalogService {
// Get old value
const oldResult = await client.query(`
SELECT data FROM vehicle_dropdown_cache WHERE cache_key = $1
SELECT data, created_at FROM vehicle_dropdown_cache WHERE cache_key = $1
`, [`catalog:trims:${trimId}`]);
if (oldResult.rows.length === 0) {
throw new Error(`Trim ${trimId} not found`);
}
const oldValue = oldResult.rows[0].data;
const newValue: CatalogTrim = { id: trimId, yearId, name };
const oldRow = oldResult.rows[0];
const oldValue = oldRow.data;
const createdAt =
oldValue?.createdAt ?? this.toIsoDate(oldRow.created_at);
const newValue: CatalogTrim = {
id: trimId,
yearId,
name,
createdAt,
updatedAt: new Date().toISOString(),
};
// Update trim
await client.query(`
@@ -750,7 +837,7 @@ export class VehicleCatalogService {
async getEnginesByTrim(trimId: number): Promise<CatalogEngine[]> {
const query = `
SELECT cache_key, data
SELECT cache_key, data, created_at, updated_at
FROM vehicle_dropdown_cache
WHERE cache_key LIKE 'catalog:engines:%'
AND (data->>'trimId')::int = $1
@@ -763,7 +850,11 @@ export class VehicleCatalogService {
id: parseInt(row.cache_key.split(':')[2]),
trimId: row.data.trimId,
name: row.data.name,
description: row.data.description
description: row.data.description,
createdAt:
row.data?.createdAt ?? this.toIsoDate(row.created_at),
updatedAt:
row.data?.updatedAt ?? this.toIsoDate(row.updated_at),
}));
} catch (error) {
logger.error('Error getting engines by trim', { error, trimId });
@@ -795,7 +886,15 @@ export class VehicleCatalogService {
const engineId = idResult.rows[0].next_id;
// Insert engine
const engine: CatalogEngine = { id: engineId, trimId, name, description };
const now = new Date().toISOString();
const engine: CatalogEngine = {
id: engineId,
trimId,
name,
description,
createdAt: now,
updatedAt: now,
};
await client.query(`
INSERT INTO vehicle_dropdown_cache (cache_key, data, expires_at)
VALUES ($1, $2, NOW() + INTERVAL '10 years')
@@ -837,15 +936,25 @@ export class VehicleCatalogService {
// Get old value
const oldResult = await client.query(`
SELECT data FROM vehicle_dropdown_cache WHERE cache_key = $1
SELECT data, created_at FROM vehicle_dropdown_cache WHERE cache_key = $1
`, [`catalog:engines:${engineId}`]);
if (oldResult.rows.length === 0) {
throw new Error(`Engine ${engineId} not found`);
}
const oldValue = oldResult.rows[0].data;
const newValue: CatalogEngine = { id: engineId, trimId, name, description };
const oldRow = oldResult.rows[0];
const oldValue = oldRow.data;
const createdAt =
oldValue?.createdAt ?? this.toIsoDate(oldRow.created_at);
const newValue: CatalogEngine = {
id: engineId,
trimId,
name,
description,
createdAt,
updatedAt: new Date().toISOString(),
};
// Update engine
await client.query(`
@@ -915,6 +1024,17 @@ export class VehicleCatalogService {
// HELPER METHODS
private toIsoDate(value: Date | string | null | undefined): string {
if (!value) {
return new Date().toISOString();
}
if (value instanceof Date) {
return value.toISOString();
}
const parsed = new Date(value);
return Number.isNaN(parsed.valueOf()) ? new Date().toISOString() : parsed.toISOString();
}
private async logChange(
client: any,
changeType: 'CREATE' | 'UPDATE' | 'DELETE',