import { Request, Response } from 'express'; import { Holiday, HolidayType } from '@models/Holiday'; import { holidayService } from '@services/holiday.service'; import { sequelize } from '@config/database'; import { QueryTypes } from 'sequelize'; import logger from '@utils/logger'; import { initializeHolidaysCache, clearWorkingHoursCache } from '@utils/tatTimeUtils'; import { clearConfigCache } from '@services/configReader.service'; /** * Get all holidays (with optional year filter) */ export const getAllHolidays = async (req: Request, res: Response): Promise => { try { const { year } = req.query; const yearNum = year ? parseInt(year as string) : undefined; const holidays = await holidayService.getAllActiveHolidays(yearNum); res.json({ success: true, data: holidays, count: holidays.length }); } catch (error) { logger.error('[Admin] Error fetching holidays:', error); res.status(500).json({ success: false, error: 'Failed to fetch holidays' }); } }; /** * Get holiday calendar for a specific year */ export const getHolidayCalendar = async (req: Request, res: Response): Promise => { try { const { year } = req.params; const yearNum = parseInt(year); if (isNaN(yearNum) || yearNum < 2000 || yearNum > 2100) { res.status(400).json({ success: false, error: 'Invalid year' }); return; } const calendar = await holidayService.getHolidayCalendar(yearNum); res.json({ success: true, year: yearNum, holidays: calendar, count: calendar.length }); } catch (error) { logger.error('[Admin] Error fetching holiday calendar:', error); res.status(500).json({ success: false, error: 'Failed to fetch holiday calendar' }); } }; /** * Create a new holiday */ export const createHoliday = async (req: Request, res: Response): Promise => { try { const userId = req.user?.userId; if (!userId) { res.status(401).json({ success: false, error: 'User not authenticated' }); return; } const { holidayDate, holidayName, description, holidayType, isRecurring, recurrenceRule, appliesToDepartments, appliesToLocations } = req.body; // Validate required fields if (!holidayDate || !holidayName) { res.status(400).json({ success: false, error: 'Holiday date and name are required' }); return; } const holiday = await holidayService.createHoliday({ holidayDate, holidayName, description, holidayType: holidayType || HolidayType.ORGANIZATIONAL, isRecurring: isRecurring || false, recurrenceRule, appliesToDepartments, appliesToLocations, createdBy: userId }); // Reload holidays cache await initializeHolidaysCache(); res.status(201).json({ success: true, message: 'Holiday created successfully', data: holiday }); } catch (error: any) { logger.error('[Admin] Error creating holiday:', error); res.status(500).json({ success: false, error: error.message || 'Failed to create holiday' }); } }; /** * Update a holiday */ export const updateHoliday = async (req: Request, res: Response): Promise => { try { const userId = req.user?.userId; if (!userId) { res.status(401).json({ success: false, error: 'User not authenticated' }); return; } const { holidayId } = req.params; const updates = req.body; const holiday = await holidayService.updateHoliday(holidayId, updates, userId); if (!holiday) { res.status(404).json({ success: false, error: 'Holiday not found' }); return; } // Reload holidays cache await initializeHolidaysCache(); res.json({ success: true, message: 'Holiday updated successfully', data: holiday }); } catch (error: any) { logger.error('[Admin] Error updating holiday:', error); res.status(500).json({ success: false, error: error.message || 'Failed to update holiday' }); } }; /** * Delete (deactivate) a holiday */ export const deleteHoliday = async (req: Request, res: Response): Promise => { try { const { holidayId } = req.params; await holidayService.deleteHoliday(holidayId); // Reload holidays cache await initializeHolidaysCache(); res.json({ success: true, message: 'Holiday deleted successfully' }); } catch (error: any) { logger.error('[Admin] Error deleting holiday:', error); res.status(500).json({ success: false, error: error.message || 'Failed to delete holiday' }); } }; /** * Bulk import holidays from CSV/JSON */ export const bulkImportHolidays = async (req: Request, res: Response): Promise => { try { const userId = req.user?.userId; if (!userId) { res.status(401).json({ success: false, error: 'User not authenticated' }); return; } const { holidays } = req.body; if (!Array.isArray(holidays) || holidays.length === 0) { res.status(400).json({ success: false, error: 'Holidays array is required' }); return; } const result = await holidayService.bulkImportHolidays(holidays, userId); // Reload holidays cache await initializeHolidaysCache(); res.json({ success: true, message: `Imported ${result.success} holidays, ${result.failed} failed`, data: result }); } catch (error: any) { logger.error('[Admin] Error bulk importing holidays:', error); res.status(500).json({ success: false, error: error.message || 'Failed to import holidays' }); } }; /** * Get all admin configurations */ export const getAllConfigurations = async (req: Request, res: Response): Promise => { try { const { category } = req.query; let whereClause = ''; if (category) { whereClause = `WHERE config_category = '${category}'`; } const rawConfigurations = await sequelize.query(` SELECT config_id, config_key, config_category, config_value, value_type, display_name, description, default_value, is_editable, is_sensitive, validation_rules, ui_component, options, sort_order, requires_restart, last_modified_at, last_modified_by FROM admin_configurations ${whereClause} ORDER BY config_category, sort_order `, { type: QueryTypes.SELECT }); // Map snake_case to camelCase for frontend const configurations = (rawConfigurations as any[]).map((config: any) => ({ configId: config.config_id, configKey: config.config_key, configCategory: config.config_category, configValue: config.config_value, valueType: config.value_type, displayName: config.display_name, description: config.description, defaultValue: config.default_value, isEditable: config.is_editable, isSensitive: config.is_sensitive || false, validationRules: config.validation_rules, uiComponent: config.ui_component, options: config.options, sortOrder: config.sort_order, requiresRestart: config.requires_restart || false, lastModifiedAt: config.last_modified_at, lastModifiedBy: config.last_modified_by })); res.json({ success: true, data: configurations, count: configurations.length }); } catch (error) { logger.error('[Admin] Error fetching configurations:', error); res.status(500).json({ success: false, error: 'Failed to fetch configurations' }); } }; /** * Update a configuration */ export const updateConfiguration = async (req: Request, res: Response): Promise => { try { const userId = req.user?.userId; if (!userId) { res.status(401).json({ success: false, error: 'User not authenticated' }); return; } const { configKey } = req.params; const { configValue } = req.body; if (configValue === undefined) { res.status(400).json({ success: false, error: 'Config value is required' }); return; } // Update configuration const result = await sequelize.query(` UPDATE admin_configurations SET config_value = :configValue, last_modified_by = :userId, last_modified_at = NOW(), updated_at = NOW() WHERE config_key = :configKey AND is_editable = true RETURNING * `, { replacements: { configValue, userId, configKey }, type: QueryTypes.UPDATE }); if (!result || (result[1] as any) === 0) { res.status(404).json({ success: false, error: 'Configuration not found or not editable' }); return; } // Clear config cache so new values are used immediately clearConfigCache(); // If working hours config was updated, also clear working hours cache const workingHoursKeys = ['WORK_START_HOUR', 'WORK_END_HOUR', 'WORK_START_DAY', 'WORK_END_DAY']; if (workingHoursKeys.includes(configKey)) { await clearWorkingHoursCache(); logger.info(`[Admin] Working hours configuration '${configKey}' updated - cache cleared and reloaded`); } else { logger.info(`[Admin] Configuration '${configKey}' updated and cache cleared`); } res.json({ success: true, message: 'Configuration updated successfully' }); } catch (error: any) { logger.error('[Admin] Error updating configuration:', error); res.status(500).json({ success: false, error: error.message || 'Failed to update configuration' }); } }; /** * Reset configuration to default value */ export const resetConfiguration = async (req: Request, res: Response): Promise => { try { const { configKey } = req.params; await sequelize.query(` UPDATE admin_configurations SET config_value = default_value, updated_at = NOW() WHERE config_key = :configKey `, { replacements: { configKey }, type: QueryTypes.UPDATE }); // Clear config cache so reset values are used immediately clearConfigCache(); // If working hours config was reset, also clear working hours cache const workingHoursKeys = ['WORK_START_HOUR', 'WORK_END_HOUR', 'WORK_START_DAY', 'WORK_END_DAY']; if (workingHoursKeys.includes(configKey)) { await clearWorkingHoursCache(); logger.info(`[Admin] Working hours configuration '${configKey}' reset to default - cache cleared and reloaded`); } else { logger.info(`[Admin] Configuration '${configKey}' reset to default and cache cleared`); } res.json({ success: true, message: 'Configuration reset to default' }); } catch (error: any) { logger.error('[Admin] Error resetting configuration:', error); res.status(500).json({ success: false, error: error.message || 'Failed to reset configuration' }); } };