feat(012-admin-list-views): GET /admin/business-calendars/:id now includes holidays

Follow-up to 012-admin-list-views, discovered while building
supporthub-web's own SLA/calendar admin screen (001-agent-admin-ui User
Story 4): holidays could only be added or removed, never read back -
GET /admin/business-calendars/:id returned the bare calendar with no
way to display what holidays were already on file. The repository
already had findByIdWithHolidays; it just wasn't wired to this route.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
This commit is contained in:
saqib mir
2026-09-07 16:40:00 +05:30
co-authored by Claude Sonnet 5
parent 2034966d6d
commit 249e7cd0ce
3 changed files with 69 additions and 1 deletions
@@ -22,7 +22,7 @@ export class BusinessCalendarsController {
async getById(request: FastifyRequest, reply: FastifyReply) {
const { id } = request.params as { id: string };
const calendar = await this.service.getById(id);
const calendar = await this.service.getByIdWithHolidays(id);
return reply.status(200).send({ success: true, data: calendar, meta: null });
}
@@ -34,6 +34,14 @@ export class BusinessCalendarsService {
return calendar;
}
/** 012-admin-list-views follow-up: GET /admin/business-calendars/:id's own response — a
* calendar's holidays had no way to be read back at all before this (only added/removed). */
async getByIdWithHolidays(id: string): Promise<BusinessCalendar & { holidays: Holiday[] }> {
const calendar = await this.calendars.findByIdWithHolidays(id);
if (!calendar) throw new NotFoundError('Business calendar not found.');
return calendar;
}
async list(): Promise<BusinessCalendar[]> {
return this.calendars.findAll();
}
@@ -0,0 +1,60 @@
import { describe, it, expect, beforeAll, afterAll } from 'vitest';
import { buildApp } from '@/app';
import { prismaClient } from '@/infrastructure/database';
import { FastifyInstance } from 'fastify';
import { loginAs, authHeader } from '../helpers/auth';
/**
* Covers specs/008-sla-escalation's business-calendar create/holiday flow, plus
* 012-admin-list-views' own follow-up: GET /admin/business-calendars/:id now includes holidays
* (previously only addable/removable, never readable back).
*/
describe('Business calendars — create, add a holiday, and read both back', () => {
let app: FastifyInstance;
let token: string;
let calendarId: string;
beforeAll(async () => {
app = await buildApp();
token = await loginAs(app, 'ADMIN');
});
afterAll(async () => {
await prismaClient.holiday.deleteMany({ where: { calendarId } });
await prismaClient.businessCalendar.deleteMany({ where: { id: calendarId } });
await app.close();
});
it('a created calendar and an added holiday are both displayed back exactly as entered', async () => {
const created = await app.inject({
method: 'POST',
url: '/admin/business-calendars',
headers: authHeader(token),
payload: {
name: `Test Calendar ${Date.now()}`,
timezone: 'America/New_York',
workingHours: { mon: { start: '09:00', end: '17:00' } },
},
});
expect(created.statusCode).toBe(201);
calendarId = created.json().data.id;
const holiday = await app.inject({
method: 'POST',
url: `/admin/business-calendars/${calendarId}/holidays`,
headers: authHeader(token),
payload: { date: '2026-12-25', description: 'Christmas' },
});
expect(holiday.statusCode).toBe(201);
const fetched = await app.inject({
method: 'GET',
url: `/admin/business-calendars/${calendarId}`,
headers: authHeader(token),
});
expect(fetched.statusCode).toBe(200);
expect(fetched.json().data.workingHours.mon).toEqual({ start: '09:00', end: '17:00' });
expect(fetched.json().data.holidays).toHaveLength(1);
expect(fetched.json().data.holidays[0].description).toBe('Christmas');
});
});