import { and, eq } from 'drizzle-orm'; import { db } from '@/lib/db'; import { savedViews } from '@/lib/db/schema'; import { NotFoundError } from '@/lib/errors'; import type { CreateSavedViewInput, UpdateSavedViewInput } from '@/lib/validators/saved-views'; export const savedViewsService = { async list(portId: string, userId: string, entityType?: string) { const conditions = [eq(savedViews.portId, portId), eq(savedViews.userId, userId)]; if (entityType) { conditions.push(eq(savedViews.entityType, entityType)); } return db .select() .from(savedViews) .where(and(...conditions)); }, async create(portId: string, userId: string, data: CreateSavedViewInput) { if (data.isDefault) { await db .update(savedViews) .set({ isDefault: false }) .where( and( eq(savedViews.portId, portId), eq(savedViews.userId, userId), eq(savedViews.entityType, data.entityType), eq(savedViews.isDefault, true), ), ); } const [view] = await db .insert(savedViews) .values({ portId, userId, entityType: data.entityType, name: data.name, filters: data.filters ?? {}, sortConfig: data.sortConfig ?? null, columnConfig: data.columnConfig ?? null, isShared: data.isShared ?? false, isDefault: data.isDefault ?? false, }) .returning(); return view; }, async update(portId: string, userId: string, viewId: string, data: UpdateSavedViewInput) { const existing = await db.query.savedViews.findFirst({ where: and( eq(savedViews.id, viewId), eq(savedViews.portId, portId), eq(savedViews.userId, userId), ), }); if (!existing) { throw new NotFoundError('Saved view'); } if (data.isDefault) { const entityType = data.entityType ?? existing.entityType; await db .update(savedViews) .set({ isDefault: false }) .where( and( eq(savedViews.portId, portId), eq(savedViews.userId, userId), eq(savedViews.entityType, entityType), eq(savedViews.isDefault, true), ), ); } const [updated] = await db .update(savedViews) .set({ ...(data.name !== undefined && { name: data.name }), ...(data.entityType !== undefined && { entityType: data.entityType }), ...(data.filters !== undefined && { filters: data.filters }), ...(data.sortConfig !== undefined && { sortConfig: data.sortConfig }), ...(data.columnConfig !== undefined && { columnConfig: data.columnConfig }), ...(data.isShared !== undefined && { isShared: data.isShared }), ...(data.isDefault !== undefined && { isDefault: data.isDefault }), updatedAt: new Date(), }) .where( and( eq(savedViews.id, viewId), eq(savedViews.portId, portId), eq(savedViews.userId, userId), ), ) .returning(); return updated; }, async delete(portId: string, userId: string, viewId: string) { const existing = await db.query.savedViews.findFirst({ where: and( eq(savedViews.id, viewId), eq(savedViews.portId, portId), eq(savedViews.userId, userId), ), }); if (!existing) { throw new NotFoundError('Saved view'); } await db .delete(savedViews) .where( and( eq(savedViews.id, viewId), eq(savedViews.portId, portId), eq(savedViews.userId, userId), ), ); }, async setDefault(portId: string, userId: string, entityType: string, viewId: string) { const existing = await db.query.savedViews.findFirst({ where: and( eq(savedViews.id, viewId), eq(savedViews.portId, portId), eq(savedViews.userId, userId), eq(savedViews.entityType, entityType), ), }); if (!existing) { throw new NotFoundError('Saved view'); } // Unset any existing default for this entityType + user + port await db .update(savedViews) .set({ isDefault: false }) .where( and( eq(savedViews.portId, portId), eq(savedViews.userId, userId), eq(savedViews.entityType, entityType), eq(savedViews.isDefault, true), ), ); const [updated] = await db .update(savedViews) .set({ isDefault: true, updatedAt: new Date() }) .where( and( eq(savedViews.id, viewId), eq(savedViews.portId, portId), eq(savedViews.userId, userId), ), ) .returning(); return updated; }, };