Files
pn-new-crm/src/lib/services/saved-views.service.ts
Matt 67d7e6e3d5
Some checks failed
Build & Push Docker Images / build-and-push (push) Has been cancelled
Build & Push Docker Images / deploy (push) Has been cancelled
Build & Push Docker Images / lint (push) Has been cancelled
Initial commit: Port Nimara CRM (Layers 0-4)
Full CRM rebuild with Next.js 15, TypeScript, Tailwind, Drizzle ORM,
PostgreSQL, Redis, BullMQ, MinIO, and Socket.io. Includes 461 source
files covering clients, berths, interests/pipeline, documents/EOI,
expenses/invoices, email, notifications, dashboard, admin, and
client portal. CI/CD via Gitea Actions with Docker builds.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-26 11:52:51 +01:00

174 lines
4.7 KiB
TypeScript

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;
},
};