Re_Backend/src/controllers/admin.controller.ts

429 lines
11 KiB
TypeScript

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<void> => {
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<void> => {
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<void> => {
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<void> => {
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<void> => {
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<void> => {
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<void> => {
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<void> => {
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<void> => {
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'
});
}
};