"use strict"; var __importDefault = (this && this.__importDefault) || function (mod) { return (mod && mod.__esModule) ? mod : { "default": mod }; }; Object.defineProperty(exports, "__esModule", { value: true }); const database_1 = __importDefault(require("../config/database")); const databaseService_1 = __importDefault(require("./databaseService")); class EnhancedDataService { // VIP operations async getVips() { try { const query = ` SELECT v.*, COALESCE( json_agg( json_build_object( 'flightNumber', f.flight_number, 'flightDate', f.flight_date, 'segment', f.segment ) ORDER BY f.segment ) FILTER (WHERE f.id IS NOT NULL), '[]'::json ) as flights FROM vips v LEFT JOIN flights f ON v.id = f.vip_id GROUP BY v.id ORDER BY v.name `; const result = await database_1.default.query(query); return result.rows.map(row => ({ id: row.id, name: row.name, organization: row.organization, department: row.department, transportMode: row.transport_mode, expectedArrival: row.expected_arrival, needsAirportPickup: row.needs_airport_pickup, needsVenueTransport: row.needs_venue_transport, notes: row.notes, flights: row.flights })); } catch (error) { console.error('❌ Error fetching VIPs:', error); throw error; } } async addVip(vip) { const client = await database_1.default.connect(); try { await client.query('BEGIN'); // Insert VIP const vipQuery = ` INSERT INTO vips (id, name, organization, department, transport_mode, expected_arrival, needs_airport_pickup, needs_venue_transport, notes) VALUES ($1, $2, $3, $4, $5, $6, $7, $8, $9) RETURNING * `; const vipResult = await client.query(vipQuery, [ vip.id, vip.name, vip.organization, vip.department || 'Office of Development', vip.transportMode, vip.expectedArrival || null, vip.needsAirportPickup || false, vip.needsVenueTransport, vip.notes || '' ]); // Insert flights if any if (vip.flights && vip.flights.length > 0) { for (const flight of vip.flights) { const flightQuery = ` INSERT INTO flights (vip_id, flight_number, flight_date, segment) VALUES ($1, $2, $3, $4) `; await client.query(flightQuery, [ vip.id, flight.flightNumber, flight.flightDate, flight.segment ]); } } await client.query('COMMIT'); const savedVip = { ...vip, department: vipResult.rows[0].department, transportMode: vipResult.rows[0].transport_mode, expectedArrival: vipResult.rows[0].expected_arrival, needsAirportPickup: vipResult.rows[0].needs_airport_pickup, needsVenueTransport: vipResult.rows[0].needs_venue_transport }; return savedVip; } catch (error) { await client.query('ROLLBACK'); console.error('❌ Error adding VIP:', error); throw error; } finally { client.release(); } } async updateVip(id, vip) { const client = await database_1.default.connect(); try { await client.query('BEGIN'); // Update VIP const vipQuery = ` UPDATE vips SET name = $2, organization = $3, department = $4, transport_mode = $5, expected_arrival = $6, needs_airport_pickup = $7, needs_venue_transport = $8, notes = $9 WHERE id = $1 RETURNING * `; const vipResult = await client.query(vipQuery, [ id, vip.name, vip.organization, vip.department || 'Office of Development', vip.transportMode, vip.expectedArrival || null, vip.needsAirportPickup || false, vip.needsVenueTransport, vip.notes || '' ]); if (vipResult.rows.length === 0) { await client.query('ROLLBACK'); return null; } // Delete existing flights and insert new ones await client.query('DELETE FROM flights WHERE vip_id = $1', [id]); if (vip.flights && vip.flights.length > 0) { for (const flight of vip.flights) { const flightQuery = ` INSERT INTO flights (vip_id, flight_number, flight_date, segment) VALUES ($1, $2, $3, $4) `; await client.query(flightQuery, [ id, flight.flightNumber, flight.flightDate, flight.segment ]); } } await client.query('COMMIT'); const updatedVip = { id: vipResult.rows[0].id, name: vipResult.rows[0].name, organization: vipResult.rows[0].organization, department: vipResult.rows[0].department, transportMode: vipResult.rows[0].transport_mode, expectedArrival: vipResult.rows[0].expected_arrival, needsAirportPickup: vipResult.rows[0].needs_airport_pickup, needsVenueTransport: vipResult.rows[0].needs_venue_transport, notes: vipResult.rows[0].notes, flights: vip.flights || [] }; return updatedVip; } catch (error) { await client.query('ROLLBACK'); console.error('❌ Error updating VIP:', error); throw error; } finally { client.release(); } } async deleteVip(id) { try { const query = ` DELETE FROM vips WHERE id = $1 RETURNING * `; const result = await database_1.default.query(query, [id]); if (result.rows.length === 0) { return null; } const deletedVip = { id: result.rows[0].id, name: result.rows[0].name, organization: result.rows[0].organization, department: result.rows[0].department, transportMode: result.rows[0].transport_mode, expectedArrival: result.rows[0].expected_arrival, needsAirportPickup: result.rows[0].needs_airport_pickup, needsVenueTransport: result.rows[0].needs_venue_transport, notes: result.rows[0].notes }; return deletedVip; } catch (error) { console.error('❌ Error deleting VIP:', error); throw error; } } // Driver operations async getDrivers() { try { const query = ` SELECT d.*, COALESCE( json_agg(DISTINCT se.vip_id) FILTER (WHERE se.vip_id IS NOT NULL), '[]'::json ) as assigned_vip_ids FROM drivers d LEFT JOIN schedule_events se ON d.id = se.assigned_driver_id GROUP BY d.id ORDER BY d.name `; const result = await database_1.default.query(query); // Get current locations from Redis const driversWithLocations = await Promise.all(result.rows.map(async (row) => { const location = await databaseService_1.default.getDriverLocation(row.id); return { id: row.id, name: row.name, phone: row.phone, department: row.department, currentLocation: location ? { lat: location.lat, lng: location.lng } : { lat: 0, lng: 0 }, assignedVipIds: row.assigned_vip_ids || [] }; })); return driversWithLocations; } catch (error) { console.error('❌ Error fetching drivers:', error); throw error; } } async addDriver(driver) { try { const query = ` INSERT INTO drivers (id, name, phone, department) VALUES ($1, $2, $3, $4) RETURNING * `; const result = await database_1.default.query(query, [ driver.id, driver.name, driver.phone, driver.department || 'Office of Development' ]); // Store location in Redis if provided if (driver.currentLocation) { await databaseService_1.default.updateDriverLocation(driver.id, driver.currentLocation); } const savedDriver = { id: result.rows[0].id, name: result.rows[0].name, phone: result.rows[0].phone, department: result.rows[0].department, currentLocation: driver.currentLocation || { lat: 0, lng: 0 } }; return savedDriver; } catch (error) { console.error('❌ Error adding driver:', error); throw error; } } async updateDriver(id, driver) { try { const query = ` UPDATE drivers SET name = $2, phone = $3, department = $4 WHERE id = $1 RETURNING * `; const result = await database_1.default.query(query, [ id, driver.name, driver.phone, driver.department || 'Office of Development' ]); if (result.rows.length === 0) { return null; } // Update location in Redis if provided if (driver.currentLocation) { await databaseService_1.default.updateDriverLocation(id, driver.currentLocation); } const updatedDriver = { id: result.rows[0].id, name: result.rows[0].name, phone: result.rows[0].phone, department: result.rows[0].department, currentLocation: driver.currentLocation || { lat: 0, lng: 0 } }; return updatedDriver; } catch (error) { console.error('❌ Error updating driver:', error); throw error; } } async deleteDriver(id) { try { const query = ` DELETE FROM drivers WHERE id = $1 RETURNING * `; const result = await database_1.default.query(query, [id]); if (result.rows.length === 0) { return null; } const deletedDriver = { id: result.rows[0].id, name: result.rows[0].name, phone: result.rows[0].phone, department: result.rows[0].department }; return deletedDriver; } catch (error) { console.error('❌ Error deleting driver:', error); throw error; } } // Schedule operations async getSchedule(vipId) { try { const query = ` SELECT * FROM schedule_events WHERE vip_id = $1 ORDER BY start_time `; const result = await database_1.default.query(query, [vipId]); return result.rows.map(row => ({ id: row.id, title: row.title, location: row.location, startTime: row.start_time, endTime: row.end_time, description: row.description, assignedDriverId: row.assigned_driver_id, status: row.status, type: row.event_type })); } catch (error) { console.error('❌ Error fetching schedule:', error); throw error; } } async addScheduleEvent(vipId, event) { try { const query = ` INSERT INTO schedule_events (id, vip_id, title, location, start_time, end_time, description, assigned_driver_id, status, event_type) VALUES ($1, $2, $3, $4, $5, $6, $7, $8, $9, $10) RETURNING * `; const result = await database_1.default.query(query, [ event.id, vipId, event.title, event.location, event.startTime, event.endTime, event.description || '', event.assignedDriverId || null, event.status, event.type ]); const savedEvent = { id: result.rows[0].id, title: result.rows[0].title, location: result.rows[0].location, startTime: result.rows[0].start_time, endTime: result.rows[0].end_time, description: result.rows[0].description, assignedDriverId: result.rows[0].assigned_driver_id, status: result.rows[0].status, type: result.rows[0].event_type }; return savedEvent; } catch (error) { console.error('❌ Error adding schedule event:', error); throw error; } } async updateScheduleEvent(vipId, eventId, event) { try { const query = ` UPDATE schedule_events SET title = $3, location = $4, start_time = $5, end_time = $6, description = $7, assigned_driver_id = $8, status = $9, event_type = $10 WHERE id = $1 AND vip_id = $2 RETURNING * `; const result = await database_1.default.query(query, [ eventId, vipId, event.title, event.location, event.startTime, event.endTime, event.description || '', event.assignedDriverId || null, event.status, event.type ]); if (result.rows.length === 0) { return null; } const updatedEvent = { id: result.rows[0].id, title: result.rows[0].title, location: result.rows[0].location, startTime: result.rows[0].start_time, endTime: result.rows[0].end_time, description: result.rows[0].description, assignedDriverId: result.rows[0].assigned_driver_id, status: result.rows[0].status, type: result.rows[0].event_type }; return updatedEvent; } catch (error) { console.error('❌ Error updating schedule event:', error); throw error; } } async deleteScheduleEvent(vipId, eventId) { try { const query = ` DELETE FROM schedule_events WHERE id = $1 AND vip_id = $2 RETURNING * `; const result = await database_1.default.query(query, [eventId, vipId]); if (result.rows.length === 0) { return null; } const deletedEvent = { id: result.rows[0].id, title: result.rows[0].title, location: result.rows[0].location, startTime: result.rows[0].start_time, endTime: result.rows[0].end_time, description: result.rows[0].description, assignedDriverId: result.rows[0].assigned_driver_id, status: result.rows[0].status, type: result.rows[0].event_type }; return deletedEvent; } catch (error) { console.error('❌ Error deleting schedule event:', error); throw error; } } async getAllSchedules() { try { const query = ` SELECT * FROM schedule_events ORDER BY vip_id, start_time `; const result = await database_1.default.query(query); const schedules = {}; for (const row of result.rows) { const vipId = row.vip_id; if (!schedules[vipId]) { schedules[vipId] = []; } schedules[vipId].push({ id: row.id, title: row.title, location: row.location, startTime: row.start_time, endTime: row.end_time, description: row.description, assignedDriverId: row.assigned_driver_id, status: row.status, type: row.event_type }); } return schedules; } catch (error) { console.error('❌ Error fetching all schedules:', error); throw error; } } // Admin settings operations async getAdminSettings() { try { const query = ` SELECT setting_key, setting_value FROM admin_settings `; const result = await database_1.default.query(query); // Default settings structure const defaultSettings = { apiKeys: { aviationStackKey: '', googleMapsKey: '', twilioKey: '', googleClientId: '', googleClientSecret: '' }, systemSettings: { defaultPickupLocation: '', defaultDropoffLocation: '', timeZone: 'America/New_York', notificationsEnabled: false } }; // If no settings exist, return defaults if (result.rows.length === 0) { return defaultSettings; } // Reconstruct nested object from flattened keys const settings = { ...defaultSettings }; for (const row of result.rows) { const keys = row.setting_key.split('.'); let current = settings; for (let i = 0; i < keys.length - 1; i++) { if (!current[keys[i]]) { current[keys[i]] = {}; } current = current[keys[i]]; } // Parse boolean values let value = row.setting_value; if (value === 'true') value = true; else if (value === 'false') value = false; current[keys[keys.length - 1]] = value; } return settings; } catch (error) { console.error('❌ Error fetching admin settings:', error); throw error; } } async updateAdminSettings(settings) { try { // Flatten settings and update const flattenSettings = (obj, prefix = '') => { const result = []; for (const [key, value] of Object.entries(obj)) { const fullKey = prefix ? `${prefix}.${key}` : key; if (typeof value === 'object' && value !== null) { result.push(...flattenSettings(value, fullKey)); } else { result.push({ key: fullKey, value: String(value) }); } } return result; }; const flatSettings = flattenSettings(settings); for (const setting of flatSettings) { const query = ` INSERT INTO admin_settings (setting_key, setting_value) VALUES ($1, $2) ON CONFLICT (setting_key) DO UPDATE SET setting_value = $2 `; await database_1.default.query(query, [setting.key, setting.value]); } } catch (error) { console.error('❌ Error updating admin settings:', error); throw error; } } } exports.default = new EnhancedDataService(); //# sourceMappingURL=enhancedDataService.js.map