- Add DomainsModule with full CRUD, search, and activity logging - Add IdeasModule with quick capture endpoint - Add LayoutsModule for user dashboard layouts - Add WidgetsModule for widget definitions (read-only) - Update ActivityService with domain/idea logging methods - Register all new modules in AppModule
226 lines
5.0 KiB
TypeScript
226 lines
5.0 KiB
TypeScript
import { Injectable, NotFoundException } from "@nestjs/common";
|
|
import { Prisma } from "@prisma/client";
|
|
import { PrismaService } from "../prisma/prisma.service";
|
|
import { ActivityService } from "../activity/activity.service";
|
|
import { ProjectStatus } from "@prisma/client";
|
|
import type { CreateProjectDto, UpdateProjectDto, QueryProjectsDto } from "./dto";
|
|
|
|
/**
|
|
* Service for managing projects
|
|
*/
|
|
@Injectable()
|
|
export class ProjectsService {
|
|
constructor(
|
|
private readonly prisma: PrismaService,
|
|
private readonly activityService: ActivityService
|
|
) {}
|
|
|
|
/**
|
|
* Create a new project
|
|
*/
|
|
async create(
|
|
workspaceId: string,
|
|
userId: string,
|
|
createProjectDto: CreateProjectDto
|
|
) {
|
|
const data: any = {
|
|
...createProjectDto,
|
|
workspaceId,
|
|
creatorId: userId,
|
|
status: createProjectDto.status || ProjectStatus.PLANNING,
|
|
metadata: createProjectDto.metadata || {},
|
|
};
|
|
|
|
const project = await this.prisma.project.create({
|
|
data,
|
|
include: {
|
|
creator: {
|
|
select: { id: true, name: true, email: true },
|
|
},
|
|
_count: {
|
|
select: { tasks: true, events: true },
|
|
},
|
|
},
|
|
});
|
|
|
|
// Log activity
|
|
await this.activityService.logProjectCreated(workspaceId, userId, project.id, {
|
|
name: project.name,
|
|
});
|
|
|
|
return project;
|
|
}
|
|
|
|
/**
|
|
* Get paginated projects with filters
|
|
*/
|
|
async findAll(query: QueryProjectsDto) {
|
|
const page = query.page || 1;
|
|
const limit = query.limit || 50;
|
|
const skip = (page - 1) * limit;
|
|
|
|
// Build where clause
|
|
const where: any = {
|
|
workspaceId: query.workspaceId,
|
|
};
|
|
|
|
if (query.status) {
|
|
where.status = query.status;
|
|
}
|
|
|
|
if (query.startDateFrom || query.startDateTo) {
|
|
where.startDate = {};
|
|
if (query.startDateFrom) {
|
|
where.startDate.gte = query.startDateFrom;
|
|
}
|
|
if (query.startDateTo) {
|
|
where.startDate.lte = query.startDateTo;
|
|
}
|
|
}
|
|
|
|
// Execute queries in parallel
|
|
const [data, total] = await Promise.all([
|
|
this.prisma.project.findMany({
|
|
where,
|
|
include: {
|
|
creator: {
|
|
select: { id: true, name: true, email: true },
|
|
},
|
|
_count: {
|
|
select: { tasks: true, events: true },
|
|
},
|
|
},
|
|
orderBy: {
|
|
createdAt: "desc",
|
|
},
|
|
skip,
|
|
take: limit,
|
|
}),
|
|
this.prisma.project.count({ where }),
|
|
]);
|
|
|
|
return {
|
|
data,
|
|
meta: {
|
|
total,
|
|
page,
|
|
limit,
|
|
totalPages: Math.ceil(total / limit),
|
|
},
|
|
};
|
|
}
|
|
|
|
/**
|
|
* Get a single project by ID
|
|
*/
|
|
async findOne(id: string, workspaceId: string) {
|
|
const project = await this.prisma.project.findUnique({
|
|
where: {
|
|
id,
|
|
workspaceId,
|
|
},
|
|
include: {
|
|
creator: {
|
|
select: { id: true, name: true, email: true },
|
|
},
|
|
tasks: {
|
|
select: {
|
|
id: true,
|
|
title: true,
|
|
status: true,
|
|
priority: true,
|
|
dueDate: true,
|
|
},
|
|
orderBy: { sortOrder: "asc" },
|
|
},
|
|
events: {
|
|
select: {
|
|
id: true,
|
|
title: true,
|
|
startTime: true,
|
|
endTime: true,
|
|
},
|
|
orderBy: { startTime: "asc" },
|
|
},
|
|
_count: {
|
|
select: { tasks: true, events: true },
|
|
},
|
|
},
|
|
});
|
|
|
|
if (!project) {
|
|
throw new NotFoundException(`Project with ID ${id} not found`);
|
|
}
|
|
|
|
return project;
|
|
}
|
|
|
|
/**
|
|
* Update a project
|
|
*/
|
|
async update(
|
|
id: string,
|
|
workspaceId: string,
|
|
userId: string,
|
|
updateProjectDto: UpdateProjectDto
|
|
) {
|
|
// Verify project exists
|
|
const existingProject = await this.prisma.project.findUnique({
|
|
where: { id, workspaceId },
|
|
});
|
|
|
|
if (!existingProject) {
|
|
throw new NotFoundException(`Project with ID ${id} not found`);
|
|
}
|
|
|
|
const project = await this.prisma.project.update({
|
|
where: {
|
|
id,
|
|
workspaceId,
|
|
},
|
|
data: updateProjectDto as any,
|
|
include: {
|
|
creator: {
|
|
select: { id: true, name: true, email: true },
|
|
},
|
|
_count: {
|
|
select: { tasks: true, events: true },
|
|
},
|
|
},
|
|
});
|
|
|
|
// Log activity
|
|
await this.activityService.logProjectUpdated(workspaceId, userId, id, {
|
|
changes: updateProjectDto as Prisma.JsonValue,
|
|
});
|
|
|
|
return project;
|
|
}
|
|
|
|
/**
|
|
* Delete a project
|
|
*/
|
|
async remove(id: string, workspaceId: string, userId: string) {
|
|
// Verify project exists
|
|
const project = await this.prisma.project.findUnique({
|
|
where: { id, workspaceId },
|
|
});
|
|
|
|
if (!project) {
|
|
throw new NotFoundException(`Project with ID ${id} not found`);
|
|
}
|
|
|
|
await this.prisma.project.delete({
|
|
where: {
|
|
id,
|
|
workspaceId,
|
|
},
|
|
});
|
|
|
|
// Log activity
|
|
await this.activityService.logProjectDeleted(workspaceId, userId, id, {
|
|
name: project.name,
|
|
});
|
|
}
|
|
}
|