feat: add domains, ideas, layouts, widgets API modules
- 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
This commit is contained in:
185
apps/api/src/layouts/layouts.service.ts
Normal file
185
apps/api/src/layouts/layouts.service.ts
Normal file
@@ -0,0 +1,185 @@
|
||||
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,
|
||||
},
|
||||
});
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user