import { Injectable, NotFoundException } from "@nestjs/common"; import { Prisma } from "@prisma/client"; import { PrismaService } from "../prisma/prisma.service"; import { ActivityService } from "../activity/activity.service"; import type { CreateEventDto, UpdateEventDto, QueryEventsDto } from "./dto"; /** * Service for managing events */ @Injectable() export class EventsService { constructor( private readonly prisma: PrismaService, private readonly activityService: ActivityService ) {} /** * Create a new event */ async create(workspaceId: string, userId: string, createEventDto: CreateEventDto) { const projectConnection = createEventDto.projectId ? { connect: { id: createEventDto.projectId } } : undefined; const data: Prisma.EventCreateInput = { title: createEventDto.title, description: createEventDto.description ?? null, startTime: createEventDto.startTime, endTime: createEventDto.endTime ?? null, location: createEventDto.location ?? null, workspace: { connect: { id: workspaceId } }, creator: { connect: { id: userId } }, allDay: createEventDto.allDay ?? false, metadata: createEventDto.metadata ? (createEventDto.metadata as unknown as Prisma.InputJsonValue) : {}, ...(projectConnection && { project: projectConnection }), }; const event = await this.prisma.event.create({ data, include: { creator: { select: { id: true, name: true, email: true }, }, project: { select: { id: true, name: true, color: true }, }, }, }); // Log activity await this.activityService.logEventCreated(workspaceId, userId, event.id, { title: event.title, }); return event; } /** * Get paginated events with filters */ async findAll(query: QueryEventsDto) { const page = query.page ?? 1; const limit = query.limit ?? 50; const skip = (page - 1) * limit; // Build where clause const where: Prisma.EventWhereInput = query.workspaceId ? { workspaceId: query.workspaceId, } : {}; if (query.projectId) { where.projectId = query.projectId; } if (query.allDay !== undefined) { where.allDay = query.allDay; } if (query.startFrom || query.startTo) { where.startTime = {}; if (query.startFrom) { where.startTime.gte = query.startFrom; } if (query.startTo) { where.startTime.lte = query.startTo; } } // Execute queries in parallel const [data, total] = await Promise.all([ this.prisma.event.findMany({ where, include: { creator: { select: { id: true, name: true, email: true }, }, project: { select: { id: true, name: true, color: true }, }, }, orderBy: { startTime: "asc", }, skip, take: limit, }), this.prisma.event.count({ where }), ]); return { data, meta: { total, page, limit, totalPages: Math.ceil(total / limit), }, }; } /** * Get a single event by ID */ async findOne(id: string, workspaceId: string) { const event = await this.prisma.event.findUnique({ where: { id, workspaceId, }, include: { creator: { select: { id: true, name: true, email: true }, }, project: { select: { id: true, name: true, color: true }, }, }, }); if (!event) { throw new NotFoundException(`Event with ID ${id} not found`); } return event; } /** * Update an event */ async update(id: string, workspaceId: string, userId: string, updateEventDto: UpdateEventDto) { // Verify event exists const existingEvent = await this.prisma.event.findUnique({ where: { id, workspaceId }, }); if (!existingEvent) { throw new NotFoundException(`Event with ID ${id} not found`); } // Build update data, only including defined fields (excluding projectId) const updateData: Prisma.EventUpdateInput = {}; if (updateEventDto.title !== undefined) updateData.title = updateEventDto.title; if (updateEventDto.description !== undefined) updateData.description = updateEventDto.description; if (updateEventDto.startTime !== undefined) updateData.startTime = updateEventDto.startTime; if (updateEventDto.endTime !== undefined) updateData.endTime = updateEventDto.endTime; if (updateEventDto.allDay !== undefined) updateData.allDay = updateEventDto.allDay; if (updateEventDto.location !== undefined) updateData.location = updateEventDto.location; if (updateEventDto.recurrence !== undefined) { updateData.recurrence = updateEventDto.recurrence as unknown as Prisma.InputJsonValue; } if (updateEventDto.metadata !== undefined) { updateData.metadata = updateEventDto.metadata as unknown as Prisma.InputJsonValue; } // Handle project relation separately if (updateEventDto.projectId !== undefined) { updateData.project = { connect: { id: updateEventDto.projectId } }; } const event = await this.prisma.event.update({ where: { id, workspaceId, }, data: updateData, include: { creator: { select: { id: true, name: true, email: true }, }, project: { select: { id: true, name: true, color: true }, }, }, }); // Log activity await this.activityService.logEventUpdated(workspaceId, userId, id, { changes: updateEventDto as Prisma.JsonValue, }); return event; } /** * Delete an event */ async remove(id: string, workspaceId: string, userId: string) { // Verify event exists const event = await this.prisma.event.findUnique({ where: { id, workspaceId }, }); if (!event) { throw new NotFoundException(`Event with ID ${id} not found`); } await this.prisma.event.delete({ where: { id, workspaceId, }, }); // Log activity await this.activityService.logEventDeleted(workspaceId, userId, id, { title: event.title, }); } }