feat(holiday-system): implement comprehensive holiday management features

- Added holiday types and organization holidays management with CRUD operations.
- Introduced country holidays import functionality using the date-holidays npm package.
- Created database migrations for holiday types and organization holidays tables.
- Developed a holiday calendar component for visual representation and management of holidays.
- Enhanced API routes for holiday-related operations and integrated them into the admin center.
- Updated frontend localization for holiday management features.
- Implemented scripts for populating holidays in the database for 200+ countries.
This commit is contained in:
chamiakJ
2025-07-16 07:59:27 +05:30
parent 737f7cada2
commit 5214368354
16 changed files with 2181 additions and 560 deletions

View File

@@ -0,0 +1,264 @@
import { IWorkLenzRequest } from '../interfaces/worklenz-request';
import { IWorkLenzResponse } from '../interfaces/worklenz-response';
import db from '../config/db';
import { ServerResponse } from '../models/server-response';
import WorklenzControllerBase from './worklenz-controller-base';
import HandleExceptions from '../decorators/handle-exceptions';
import {
ICreateHolidayRequest,
IUpdateHolidayRequest,
IImportCountryHolidaysRequest,
} from '../interfaces/holiday.interface';
export default class HolidayController extends WorklenzControllerBase {
@HandleExceptions()
public static async getHolidayTypes(req: IWorkLenzRequest, res: IWorkLenzResponse): Promise<IWorkLenzResponse> {
const q = `SELECT id, name, description, color_code, created_at, updated_at
FROM holiday_types
ORDER BY name;`;
const result = await db.query(q);
return res.status(200).send(new ServerResponse(true, result.rows));
}
@HandleExceptions()
public static async getOrganizationHolidays(req: IWorkLenzRequest, res: IWorkLenzResponse): Promise<IWorkLenzResponse> {
const { year } = req.query;
const yearFilter = year ? `AND EXTRACT(YEAR FROM date) = $2` : '';
const params = year ? [req.user?.owner_id, year] : [req.user?.owner_id];
const q = `SELECT oh.id, oh.organization_id, oh.holiday_type_id, oh.name, oh.description,
oh.date, oh.is_recurring, oh.created_at, oh.updated_at,
ht.name as holiday_type_name, ht.color_code
FROM organization_holidays oh
JOIN holiday_types ht ON oh.holiday_type_id = ht.id
WHERE oh.organization_id = (
SELECT id FROM organizations WHERE user_id = $1
) ${yearFilter}
ORDER BY oh.date;`;
const result = await db.query(q, params);
return res.status(200).send(new ServerResponse(true, result.rows));
}
@HandleExceptions()
public static async createOrganizationHoliday(req: IWorkLenzRequest, res: IWorkLenzResponse): Promise<IWorkLenzResponse> {
const { name, description, date, holiday_type_id, is_recurring = false }: ICreateHolidayRequest = req.body;
const q = `INSERT INTO organization_holidays (organization_id, holiday_type_id, name, description, date, is_recurring)
VALUES (
(SELECT id FROM organizations WHERE user_id = $1),
$2, $3, $4, $5, $6
)
RETURNING id;`;
const result = await db.query(q, [req.user?.owner_id, holiday_type_id, name, description, date, is_recurring]);
return res.status(201).send(new ServerResponse(true, result.rows[0]));
}
@HandleExceptions()
public static async updateOrganizationHoliday(req: IWorkLenzRequest, res: IWorkLenzResponse): Promise<IWorkLenzResponse> {
const { id } = req.params;
const { name, description, date, holiday_type_id, is_recurring }: IUpdateHolidayRequest = req.body;
const updateFields = [];
const values = [req.user?.owner_id, id];
let paramIndex = 3;
if (name !== undefined) {
updateFields.push(`name = $${paramIndex++}`);
values.push(name);
}
if (description !== undefined) {
updateFields.push(`description = $${paramIndex++}`);
values.push(description);
}
if (date !== undefined) {
updateFields.push(`date = $${paramIndex++}`);
values.push(date);
}
if (holiday_type_id !== undefined) {
updateFields.push(`holiday_type_id = $${paramIndex++}`);
values.push(holiday_type_id);
}
if (is_recurring !== undefined) {
updateFields.push(`is_recurring = $${paramIndex++}`);
values.push(is_recurring.toString());
}
if (updateFields.length === 0) {
return res.status(400).send(new ServerResponse(false, 'No fields to update'));
}
const q = `UPDATE organization_holidays
SET ${updateFields.join(', ')}, updated_at = CURRENT_TIMESTAMP
WHERE id = $2 AND organization_id = (
SELECT id FROM organizations WHERE user_id = $1
)
RETURNING id;`;
const result = await db.query(q, values);
if (result.rows.length === 0) {
return res.status(404).send(new ServerResponse(false, 'Holiday not found'));
}
return res.status(200).send(new ServerResponse(true, result.rows[0]));
}
@HandleExceptions()
public static async deleteOrganizationHoliday(req: IWorkLenzRequest, res: IWorkLenzResponse): Promise<IWorkLenzResponse> {
const { id } = req.params;
const q = `DELETE FROM organization_holidays
WHERE id = $2 AND organization_id = (
SELECT id FROM organizations WHERE user_id = $1
)
RETURNING id;`;
const result = await db.query(q, [req.user?.owner_id, id]);
if (result.rows.length === 0) {
return res.status(404).send(new ServerResponse(false, 'Holiday not found'));
}
return res.status(200).send(new ServerResponse(true, { message: 'Holiday deleted successfully' }));
}
@HandleExceptions()
public static async getCountryHolidays(req: IWorkLenzRequest, res: IWorkLenzResponse): Promise<IWorkLenzResponse> {
const { country_code, year } = req.query;
if (!country_code) {
return res.status(400).send(new ServerResponse(false, 'Country code is required'));
}
const yearFilter = year ? `AND EXTRACT(YEAR FROM date) = $2` : '';
const params = year ? [country_code, year] : [country_code];
const q = `SELECT id, country_code, name, description, date, is_recurring, created_at, updated_at
FROM country_holidays
WHERE country_code = $1 ${yearFilter}
ORDER BY date;`;
const result = await db.query(q, params);
return res.status(200).send(new ServerResponse(true, result.rows));
}
@HandleExceptions()
public static async getAvailableCountries(req: IWorkLenzRequest, res: IWorkLenzResponse): Promise<IWorkLenzResponse> {
const q = `SELECT DISTINCT c.code, c.name
FROM countries c
JOIN country_holidays ch ON c.code = ch.country_code
ORDER BY c.name;`;
const result = await db.query(q);
return res.status(200).send(new ServerResponse(true, result.rows));
}
@HandleExceptions()
public static async importCountryHolidays(req: IWorkLenzRequest, res: IWorkLenzResponse): Promise<IWorkLenzResponse> {
const { country_code, year }: IImportCountryHolidaysRequest = req.body;
if (!country_code) {
return res.status(400).send(new ServerResponse(false, 'Country code is required'));
}
// Get organization ID
const orgQ = `SELECT id FROM organizations WHERE user_id = $1`;
const orgResult = await db.query(orgQ, [req.user?.owner_id]);
const organizationId = orgResult.rows[0]?.id;
if (!organizationId) {
return res.status(404).send(new ServerResponse(false, 'Organization not found'));
}
// Get default holiday type (Public Holiday)
const typeQ = `SELECT id FROM holiday_types WHERE name = 'Public Holiday' LIMIT 1`;
const typeResult = await db.query(typeQ);
const holidayTypeId = typeResult.rows[0]?.id;
if (!holidayTypeId) {
return res.status(404).send(new ServerResponse(false, 'Default holiday type not found'));
}
// Get country holidays for the specified year
const yearFilter = year ? `AND EXTRACT(YEAR FROM date) = $2` : '';
const params = year ? [country_code, year] : [country_code];
const holidaysQ = `SELECT name, description, date, is_recurring
FROM country_holidays
WHERE country_code = $1 ${yearFilter}`;
const holidaysResult = await db.query(holidaysQ, params);
if (holidaysResult.rows.length === 0) {
return res.status(404).send(new ServerResponse(false, 'No holidays found for this country and year'));
}
// Import holidays to organization
const importQ = `INSERT INTO organization_holidays (organization_id, holiday_type_id, name, description, date, is_recurring)
VALUES ($1, $2, $3, $4, $5, $6)
ON CONFLICT (organization_id, date) DO NOTHING`;
let importedCount = 0;
for (const holiday of holidaysResult.rows) {
try {
await db.query(importQ, [
organizationId,
holidayTypeId,
holiday.name,
holiday.description,
holiday.date,
holiday.is_recurring
]);
importedCount++;
} catch (error) {
// Skip duplicates
continue;
}
}
return res.status(200).send(new ServerResponse(true, {
message: `Successfully imported ${importedCount} holidays`,
imported_count: importedCount
}));
}
@HandleExceptions()
public static async getHolidayCalendar(req: IWorkLenzRequest, res: IWorkLenzResponse): Promise<IWorkLenzResponse> {
const { year, month } = req.query;
if (!year || !month) {
return res.status(400).send(new ServerResponse(false, 'Year and month are required'));
}
const q = `SELECT oh.id, oh.name, oh.description, oh.date, oh.is_recurring,
ht.name as holiday_type_name, ht.color_code,
'organization' as source
FROM organization_holidays oh
JOIN holiday_types ht ON oh.holiday_type_id = ht.id
WHERE oh.organization_id = (
SELECT id FROM organizations WHERE user_id = $1
)
AND EXTRACT(YEAR FROM oh.date) = $2
AND EXTRACT(MONTH FROM oh.date) = $3
UNION ALL
SELECT ch.id, ch.name, ch.description, ch.date, ch.is_recurring,
'Public Holiday' as holiday_type_name, '#f37070' as color_code,
'country' as source
FROM country_holidays ch
JOIN organizations o ON ch.country_code = (
SELECT c.code FROM countries c WHERE c.id = o.country
)
WHERE o.user_id = $1
AND EXTRACT(YEAR FROM ch.date) = $2
AND EXTRACT(MONTH FROM ch.date) = $3
ORDER BY date;`;
const result = await db.query(q, [req.user?.owner_id, year, month]);
return res.status(200).send(new ServerResponse(true, result.rows));
}
}

View File

@@ -0,0 +1,54 @@
export interface IHolidayType {
id: string;
name: string;
description?: string;
color_code: string;
created_at: string;
updated_at: string;
}
export interface IOrganizationHoliday {
id: string;
organization_id: string;
holiday_type_id: string;
name: string;
description?: string;
date: string;
is_recurring: boolean;
created_at: string;
updated_at: string;
holiday_type?: IHolidayType;
}
export interface ICountryHoliday {
id: string;
country_code: string;
name: string;
description?: string;
date: string;
is_recurring: boolean;
created_at: string;
updated_at: string;
}
export interface ICreateHolidayRequest {
name: string;
description?: string;
date: string;
holiday_type_id: string;
is_recurring?: boolean;
}
export interface IUpdateHolidayRequest {
id: string;
name?: string;
description?: string;
date?: string;
holiday_type_id?: string;
is_recurring?: boolean;
}
export interface IImportCountryHolidaysRequest {
country_code: string;
year?: number;
}

View File

@@ -0,0 +1,26 @@
import express from 'express';
import HolidayController from '../../controllers/holiday-controller';
import safeControllerFunction from '../../shared/safe-controller-function';
import teamOwnerOrAdminValidator from '../../middlewares/validators/team-owner-or-admin-validator';
import idParamValidator from '../../middlewares/validators/id-param-validator';
const holidayApiRouter = express.Router();
// Holiday types
holidayApiRouter.get('/types', safeControllerFunction(HolidayController.getHolidayTypes));
// Organization holidays
holidayApiRouter.get('/organization', teamOwnerOrAdminValidator, safeControllerFunction(HolidayController.getOrganizationHolidays));
holidayApiRouter.post('/organization', teamOwnerOrAdminValidator, safeControllerFunction(HolidayController.createOrganizationHoliday));
holidayApiRouter.put('/organization/:id', teamOwnerOrAdminValidator, idParamValidator, safeControllerFunction(HolidayController.updateOrganizationHoliday));
holidayApiRouter.delete('/organization/:id', teamOwnerOrAdminValidator, idParamValidator, safeControllerFunction(HolidayController.deleteOrganizationHoliday));
// Country holidays
holidayApiRouter.get('/countries', safeControllerFunction(HolidayController.getAvailableCountries));
holidayApiRouter.get('/countries/:country_code', safeControllerFunction(HolidayController.getCountryHolidays));
holidayApiRouter.post('/import', teamOwnerOrAdminValidator, safeControllerFunction(HolidayController.importCountryHolidays));
// Calendar view
holidayApiRouter.get('/calendar', teamOwnerOrAdminValidator, safeControllerFunction(HolidayController.getHolidayCalendar));
export default holidayApiRouter;

View File

@@ -1,120 +1,124 @@
import express from "express";
import AccessControlsController from "../../controllers/access-controls-controller";
import AuthController from "../../controllers/auth-controller";
import LogsController from "../../controllers/logs-controller";
import OverviewController from "../../controllers/overview-controller";
import TaskPrioritiesController from "../../controllers/task-priorities-controller";
import attachmentsApiRouter from "./attachments-api-router";
import clientsApiRouter from "./clients-api-router";
import jobTitlesApiRouter from "./job-titles-api-router";
import notificationsApiRouter from "./notifications-api-router";
import personalOverviewApiRouter from "./personal-overview-api-router";
import projectMembersApiRouter from "./project-members-api-router";
import projectsApiRouter from "./projects-api-router";
import settingsApiRouter from "./settings-api-router";
import statusesApiRouter from "./statuses-api-router";
import subTasksApiRouter from "./sub-tasks-api-router";
import taskCommentsApiRouter from "./task-comments-api-router";
import taskWorkLogApiRouter from "./task-work-log-api-router";
import tasksApiRouter from "./tasks-api-router";
import teamMembersApiRouter from "./team-members-api-router";
import teamsApiRouter from "./teams-api-router";
import timezonesApiRouter from "./timezones-api-router";
import todoListApiRouter from "./todo-list-api-router";
import projectStatusesApiRouter from "./project-statuses-api-router";
import labelsApiRouter from "./labels-api-router";
import sharedProjectsApiRouter from "./shared-projects-api-router";
import resourceAllocationApiRouter from "./resource-allocation-api-router";
import taskTemplatesApiRouter from "./task-templates-api-router";
import projectInsightsApiRouter from "./project-insights-api-router";
import passwordValidator from "../../middlewares/validators/password-validator";
import adminCenterApiRouter from "./admin-center-api-router";
import reportingApiRouter from "./reporting-api-router";
import activityLogsApiRouter from "./activity-logs-api-router";
import safeControllerFunction from "../../shared/safe-controller-function";
import projectFoldersApiRouter from "./project-folders-api-router";
import taskPhasesApiRouter from "./task-phases-api-router";
import projectCategoriesApiRouter from "./project-categories-api-router";
import homePageApiRouter from "./home-page-api-router";
import ganttApiRouter from "./gantt-api-router";
import projectCommentsApiRouter from "./project-comments-api-router";
import reportingExportApiRouter from "./reporting-export-api-router";
import projectHealthsApiRouter from "./project-healths-api-router";
import ptTasksApiRouter from "./pt-tasks-api-router";
import projectTemplatesApiRouter from "./project-templates-api";
import ptTaskPhasesApiRouter from "./pt_task-phases-api-router";
import ptStatusesApiRouter from "./pt-statuses-api-router";
import workloadApiRouter from "./gannt-apis/workload-api-router";
import roadmapApiRouter from "./gannt-apis/roadmap-api-router";
import scheduleApiRouter from "./gannt-apis/schedule-api-router";
import scheduleApiV2Router from "./gannt-apis/schedule-api-v2-router";
import projectManagerApiRouter from "./project-managers-api-router";
import billingApiRouter from "./billing-api-router";
import taskDependenciesApiRouter from "./task-dependencies-api-router";
import taskRecurringApiRouter from "./task-recurring-api-router";
import express from "express";
import AccessControlsController from "../../controllers/access-controls-controller";
import AuthController from "../../controllers/auth-controller";
import LogsController from "../../controllers/logs-controller";
import OverviewController from "../../controllers/overview-controller";
import TaskPrioritiesController from "../../controllers/task-priorities-controller";
import attachmentsApiRouter from "./attachments-api-router";
import clientsApiRouter from "./clients-api-router";
import jobTitlesApiRouter from "./job-titles-api-router";
import notificationsApiRouter from "./notifications-api-router";
import personalOverviewApiRouter from "./personal-overview-api-router";
import projectMembersApiRouter from "./project-members-api-router";
import projectsApiRouter from "./projects-api-router";
import settingsApiRouter from "./settings-api-router";
import statusesApiRouter from "./statuses-api-router";
import subTasksApiRouter from "./sub-tasks-api-router";
import taskCommentsApiRouter from "./task-comments-api-router";
import taskWorkLogApiRouter from "./task-work-log-api-router";
import tasksApiRouter from "./tasks-api-router";
import teamMembersApiRouter from "./team-members-api-router";
import teamsApiRouter from "./teams-api-router";
import timezonesApiRouter from "./timezones-api-router";
import todoListApiRouter from "./todo-list-api-router";
import projectStatusesApiRouter from "./project-statuses-api-router";
import labelsApiRouter from "./labels-api-router";
import sharedProjectsApiRouter from "./shared-projects-api-router";
import resourceAllocationApiRouter from "./resource-allocation-api-router";
import taskTemplatesApiRouter from "./task-templates-api-router";
import projectInsightsApiRouter from "./project-insights-api-router";
import passwordValidator from "../../middlewares/validators/password-validator";
import adminCenterApiRouter from "./admin-center-api-router";
import reportingApiRouter from "./reporting-api-router";
import activityLogsApiRouter from "./activity-logs-api-router";
import safeControllerFunction from "../../shared/safe-controller-function";
import projectFoldersApiRouter from "./project-folders-api-router";
import taskPhasesApiRouter from "./task-phases-api-router";
import projectCategoriesApiRouter from "./project-categories-api-router";
import homePageApiRouter from "./home-page-api-router";
import ganttApiRouter from "./gantt-api-router";
import projectCommentsApiRouter from "./project-comments-api-router";
import reportingExportApiRouter from "./reporting-export-api-router";
import projectHealthsApiRouter from "./project-healths-api-router";
import ptTasksApiRouter from "./pt-tasks-api-router";
import projectTemplatesApiRouter from "./project-templates-api";
import ptTaskPhasesApiRouter from "./pt_task-phases-api-router";
import ptStatusesApiRouter from "./pt-statuses-api-router";
import workloadApiRouter from "./gannt-apis/workload-api-router";
import roadmapApiRouter from "./gannt-apis/roadmap-api-router";
import scheduleApiRouter from "./gannt-apis/schedule-api-router";
import scheduleApiV2Router from "./gannt-apis/schedule-api-v2-router";
import projectManagerApiRouter from "./project-managers-api-router";
import billingApiRouter from "./billing-api-router";
import taskDependenciesApiRouter from "./task-dependencies-api-router";
import taskRecurringApiRouter from "./task-recurring-api-router";
import customColumnsApiRouter from "./custom-columns-api-router";
import holidayApiRouter from "./holiday-api-router";
const api = express.Router();
api.use("/projects", projectsApiRouter);
api.use("/team-members", teamMembersApiRouter);
api.use("/job-titles", jobTitlesApiRouter);
api.use("/clients", clientsApiRouter);
api.use("/teams", teamsApiRouter);
api.use("/tasks", tasksApiRouter);
api.use("/settings", settingsApiRouter);
api.use("/personal-overview", personalOverviewApiRouter);
api.use("/statuses", statusesApiRouter);
api.use("/todo-list", todoListApiRouter);
api.use("/notifications", notificationsApiRouter);
api.use("/attachments", attachmentsApiRouter);
api.use("/sub-tasks", subTasksApiRouter);
api.use("/project-members", projectMembersApiRouter);
api.use("/task-time-log", taskWorkLogApiRouter);
api.use("/task-comments", taskCommentsApiRouter);
api.use("/timezones", timezonesApiRouter);
api.use("/project-statuses", projectStatusesApiRouter);
api.use("/labels", labelsApiRouter);
api.use("/resource-allocation", resourceAllocationApiRouter);
api.use("/shared/projects", sharedProjectsApiRouter);
api.use("/task-templates", taskTemplatesApiRouter);
api.use("/project-insights", projectInsightsApiRouter);
api.use("/admin-center", adminCenterApiRouter);
api.use("/reporting", reportingApiRouter);
api.use("/activity-logs", activityLogsApiRouter);
api.use("/projects-folders", projectFoldersApiRouter);
api.use("/task-phases", taskPhasesApiRouter);
api.use("/project-categories", projectCategoriesApiRouter);
api.use("/home", homePageApiRouter);
api.use("/gantt", ganttApiRouter);
api.use("/project-comments", projectCommentsApiRouter);
api.use("/reporting-export", reportingExportApiRouter);
api.use("/project-healths", projectHealthsApiRouter);
api.use("/project-templates", projectTemplatesApiRouter);
api.use("/pt-tasks", ptTasksApiRouter);
api.use("/pt-task-phases", ptTaskPhasesApiRouter);
api.use("/pt-statuses", ptStatusesApiRouter);
api.use("/workload-gannt", workloadApiRouter);
api.use("/roadmap-gannt", roadmapApiRouter);
api.use("/schedule-gannt", scheduleApiRouter);
api.use("/schedule-gannt-v2", scheduleApiV2Router);
api.use("/project-managers", projectManagerApiRouter);
api.get("/overview/:id", safeControllerFunction(OverviewController.getById));
api.get("/task-priorities", safeControllerFunction(TaskPrioritiesController.get));
api.post("/change-password", passwordValidator, safeControllerFunction(AuthController.changePassword));
api.get("/access-controls/roles", safeControllerFunction(AccessControlsController.getRoles));
api.get("/logs/my-dashboard", safeControllerFunction(LogsController.getActivityLog));
api.use("/billing", billingApiRouter);
api.use("/task-dependencies", taskDependenciesApiRouter);
api.use("/task-recurring", taskRecurringApiRouter);
const api = express.Router();
api.use("/projects", projectsApiRouter);
api.use("/team-members", teamMembersApiRouter);
api.use("/job-titles", jobTitlesApiRouter);
api.use("/clients", clientsApiRouter);
api.use("/teams", teamsApiRouter);
api.use("/tasks", tasksApiRouter);
api.use("/settings", settingsApiRouter);
api.use("/personal-overview", personalOverviewApiRouter);
api.use("/statuses", statusesApiRouter);
api.use("/todo-list", todoListApiRouter);
api.use("/notifications", notificationsApiRouter);
api.use("/attachments", attachmentsApiRouter);
api.use("/sub-tasks", subTasksApiRouter);
api.use("/project-members", projectMembersApiRouter);
api.use("/task-time-log", taskWorkLogApiRouter);
api.use("/task-comments", taskCommentsApiRouter);
api.use("/timezones", timezonesApiRouter);
api.use("/project-statuses", projectStatusesApiRouter);
api.use("/labels", labelsApiRouter);
api.use("/resource-allocation", resourceAllocationApiRouter);
api.use("/shared/projects", sharedProjectsApiRouter);
api.use("/task-templates", taskTemplatesApiRouter);
api.use("/project-insights", projectInsightsApiRouter);
api.use("/admin-center", adminCenterApiRouter);
api.use("/reporting", reportingApiRouter);
api.use("/activity-logs", activityLogsApiRouter);
api.use("/projects-folders", projectFoldersApiRouter);
api.use("/task-phases", taskPhasesApiRouter);
api.use("/project-categories", projectCategoriesApiRouter);
api.use("/home", homePageApiRouter);
api.use("/gantt", ganttApiRouter);
api.use("/project-comments", projectCommentsApiRouter);
api.use("/reporting-export", reportingExportApiRouter);
api.use("/project-healths", projectHealthsApiRouter);
api.use("/project-templates", projectTemplatesApiRouter);
api.use("/pt-tasks", ptTasksApiRouter);
api.use("/pt-task-phases", ptTaskPhasesApiRouter);
api.use("/pt-statuses", ptStatusesApiRouter);
api.use("/workload-gannt", workloadApiRouter);
api.use("/roadmap-gannt", roadmapApiRouter);
api.use("/schedule-gannt", scheduleApiRouter);
api.use("/schedule-gannt-v2", scheduleApiV2Router);
api.use("/project-managers", projectManagerApiRouter);
api.get("/overview/:id", safeControllerFunction(OverviewController.getById));
api.get("/task-priorities", safeControllerFunction(TaskPrioritiesController.get));
api.post("/change-password", passwordValidator, safeControllerFunction(AuthController.changePassword));
api.get("/access-controls/roles", safeControllerFunction(AccessControlsController.getRoles));
api.get("/logs/my-dashboard", safeControllerFunction(LogsController.getActivityLog));
api.use("/billing", billingApiRouter);
api.use("/task-dependencies", taskDependenciesApiRouter);
api.use("/task-recurring", taskRecurringApiRouter);
api.use("/custom-columns", customColumnsApiRouter);
api.use("/holidays", holidayApiRouter);
export default api;