- 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
186 lines
4.1 KiB
TypeScript
186 lines
4.1 KiB
TypeScript
import { Injectable, NotFoundException } from "@nestjs/common";
|
|
import { Prisma } from "@prisma/client";
|
|
import { PrismaService } from "../prisma/prisma.service";
|
|
import type { CreateLayoutDto, UpdateLayoutDto } from "./dto";
|
|
|
|
/**
|
|
* Service for managing user layouts
|
|
*/
|
|
@Injectable()
|
|
export class LayoutsService {
|
|
constructor(private readonly prisma: PrismaService) {}
|
|
|
|
/**
|
|
* Get all layouts for a user
|
|
*/
|
|
async findAll(workspaceId: string, userId: string) {
|
|
return this.prisma.userLayout.findMany({
|
|
where: {
|
|
workspaceId,
|
|
userId,
|
|
},
|
|
orderBy: {
|
|
isDefault: "desc",
|
|
createdAt: "desc",
|
|
},
|
|
});
|
|
}
|
|
|
|
/**
|
|
* Get the default layout for a user
|
|
*/
|
|
async findDefault(workspaceId: string, userId: string) {
|
|
const layout = await this.prisma.userLayout.findFirst({
|
|
where: {
|
|
workspaceId,
|
|
userId,
|
|
isDefault: true,
|
|
},
|
|
});
|
|
|
|
// If no default layout exists, return the most recently created one
|
|
if (!layout) {
|
|
const recentLayout = await this.prisma.userLayout.findFirst({
|
|
where: {
|
|
workspaceId,
|
|
userId,
|
|
},
|
|
orderBy: {
|
|
createdAt: "desc",
|
|
},
|
|
});
|
|
|
|
if (!recentLayout) {
|
|
throw new NotFoundException(`No layouts found for this user`);
|
|
}
|
|
|
|
return recentLayout;
|
|
}
|
|
|
|
return layout;
|
|
}
|
|
|
|
/**
|
|
* Get a single layout by ID
|
|
*/
|
|
async findOne(id: string, workspaceId: string, userId: string) {
|
|
const layout = await this.prisma.userLayout.findUnique({
|
|
where: {
|
|
id,
|
|
workspaceId,
|
|
userId,
|
|
},
|
|
});
|
|
|
|
if (!layout) {
|
|
throw new NotFoundException(`Layout with ID ${id} not found`);
|
|
}
|
|
|
|
return layout;
|
|
}
|
|
|
|
/**
|
|
* Create a new layout
|
|
*/
|
|
async create(
|
|
workspaceId: string,
|
|
userId: string,
|
|
createLayoutDto: CreateLayoutDto
|
|
) {
|
|
// Use transaction to ensure atomicity when setting default
|
|
return this.prisma.$transaction(async (tx) => {
|
|
// If setting as default, unset other defaults first
|
|
if (createLayoutDto.isDefault) {
|
|
await tx.userLayout.updateMany({
|
|
where: {
|
|
workspaceId,
|
|
userId,
|
|
isDefault: true,
|
|
},
|
|
data: {
|
|
isDefault: false,
|
|
},
|
|
});
|
|
}
|
|
|
|
return tx.userLayout.create({
|
|
data: {
|
|
...createLayoutDto,
|
|
workspaceId,
|
|
userId,
|
|
isDefault: createLayoutDto.isDefault || false,
|
|
layout: (createLayoutDto.layout || []) as unknown as Prisma.JsonValue,
|
|
} as any,
|
|
});
|
|
});
|
|
}
|
|
|
|
/**
|
|
* Update a layout
|
|
*/
|
|
async update(
|
|
id: string,
|
|
workspaceId: string,
|
|
userId: string,
|
|
updateLayoutDto: UpdateLayoutDto
|
|
) {
|
|
// Use transaction to ensure atomicity when setting default
|
|
return this.prisma.$transaction(async (tx) => {
|
|
// Verify layout exists
|
|
const existingLayout = await tx.userLayout.findUnique({
|
|
where: { id, workspaceId, userId },
|
|
});
|
|
|
|
if (!existingLayout) {
|
|
throw new NotFoundException(`Layout with ID ${id} not found`);
|
|
}
|
|
|
|
// If setting as default, unset other defaults first
|
|
if (updateLayoutDto.isDefault === true) {
|
|
await tx.userLayout.updateMany({
|
|
where: {
|
|
workspaceId,
|
|
userId,
|
|
id: { not: id },
|
|
isDefault: true,
|
|
},
|
|
data: {
|
|
isDefault: false,
|
|
},
|
|
});
|
|
}
|
|
|
|
return tx.userLayout.update({
|
|
where: {
|
|
id,
|
|
workspaceId,
|
|
userId,
|
|
},
|
|
data: updateLayoutDto as any,
|
|
});
|
|
});
|
|
}
|
|
|
|
/**
|
|
* Delete a layout
|
|
*/
|
|
async remove(id: string, workspaceId: string, userId: string) {
|
|
// Verify layout exists
|
|
const layout = await this.prisma.userLayout.findUnique({
|
|
where: { id, workspaceId, userId },
|
|
});
|
|
|
|
if (!layout) {
|
|
throw new NotFoundException(`Layout with ID ${id} not found`);
|
|
}
|
|
|
|
await this.prisma.userLayout.delete({
|
|
where: {
|
|
id,
|
|
workspaceId,
|
|
userId,
|
|
},
|
|
});
|
|
}
|
|
}
|