423 lines
12 KiB
TypeScript
423 lines
12 KiB
TypeScript
import { EntityView, EntityViewFilter, EntityViewProperty, EntityViewSort, Prisma, Property } from "@toolctrl/db/prisma-browser";
|
|
import { db } from "~/utils/db.server";
|
|
import { UserSimple } from "../users.db.server";
|
|
import UserModelHelper from "~/utils/helpers/models/UserModelHelper";
|
|
import TenantModelHelper from "~/utils/helpers/models/TenantModelHelper";
|
|
import { TenantSimple } from "../tenants.db.server";
|
|
import EntityModelHelper from "~/utils/helpers/models/EntityModelHelper";
|
|
import { EntitySimple } from "./entities.db.server";
|
|
import { FiltersDto } from "~/application/dtos/data/FiltersDto";
|
|
import EntityViewModelHelper from "~/utils/helpers/models/EntityViewModelHelper";
|
|
|
|
export type EntityViewWithDetails = EntityView & {
|
|
properties: EntityViewProperty[];
|
|
filters: EntityViewFilter[];
|
|
sort: EntityViewSort[];
|
|
groupByProperty: Property | null;
|
|
};
|
|
|
|
export type EntityViewWithTenantAndUser = EntityViewWithDetails & {
|
|
entity: EntitySimple;
|
|
createdByUser: UserSimple | null;
|
|
tenant: TenantSimple | null;
|
|
user: UserSimple | null;
|
|
};
|
|
|
|
export async function getAllEntityViews({
|
|
type,
|
|
entityId,
|
|
pagination,
|
|
filters,
|
|
}: {
|
|
type?: string;
|
|
entityId?: string;
|
|
pagination?: { pageSize: number; page: number };
|
|
filters?: FiltersDto;
|
|
}): Promise<{ items: EntityViewWithTenantAndUser[]; total: number }> {
|
|
let where: Prisma.EntityViewWhereInput = {
|
|
entityId,
|
|
};
|
|
if (type === "default") {
|
|
where = { tenantId: null, userId: null, isSystem: false };
|
|
} else if (type === "tenant") {
|
|
where = { tenantId: { not: null }, userId: null, isSystem: false };
|
|
} else if (type === "user") {
|
|
where = { userId: { not: null }, isSystem: false };
|
|
} else if (type === "system") {
|
|
where = { isSystem: true };
|
|
}
|
|
|
|
const filterEntityId = filters?.properties.find((filter) => filter.name === "entityId")?.value;
|
|
const filterTenantId = filters?.properties.find((filter) => filter.name === "tenantId")?.value;
|
|
const filterUserId = filters?.properties.find((filter) => filter.name === "userId")?.value;
|
|
if (filterEntityId) {
|
|
where = { ...where, entityId: filterEntityId };
|
|
}
|
|
if (filterTenantId) {
|
|
where = { ...where, tenantId: filterTenantId };
|
|
}
|
|
if (filterUserId) {
|
|
where = { ...where, userId: filterUserId };
|
|
}
|
|
|
|
const items = await db.entityView.findMany({
|
|
take: pagination?.pageSize,
|
|
skip: pagination ? pagination.pageSize * (pagination.page - 1) : undefined,
|
|
where,
|
|
include: {
|
|
...EntityViewModelHelper.includeDetails,
|
|
entity: { select: EntityModelHelper.selectSimpleProperties },
|
|
createdByUser: { select: UserModelHelper.selectSimpleUserProperties },
|
|
tenant: { select: TenantModelHelper.selectSimpleTenantProperties },
|
|
user: { select: UserModelHelper.selectSimpleUserProperties },
|
|
},
|
|
orderBy: [{ tenantId: "asc" }, { userId: "asc" }, { order: "asc" }],
|
|
});
|
|
const total = await db.entityView.count({ where });
|
|
return {
|
|
items,
|
|
total,
|
|
};
|
|
}
|
|
|
|
export async function getEntityViews(
|
|
entityId: string,
|
|
options?: {
|
|
tenantId?: string | null;
|
|
userId?: string | null;
|
|
}
|
|
): Promise<EntityViewWithDetails[]> {
|
|
let where: Prisma.EntityViewWhereInput = {
|
|
entityId,
|
|
isSystem: false,
|
|
};
|
|
if (options?.tenantId !== undefined) {
|
|
const userId = options.userId ?? null;
|
|
const scope: Prisma.EntityViewWhereInput[] = [{ tenantId: options.tenantId, userId: null }, { tenantId: null, userId: null }];
|
|
if (userId) {
|
|
scope.unshift({ tenantId: options.tenantId, userId });
|
|
}
|
|
where = {
|
|
...where,
|
|
OR: scope,
|
|
};
|
|
}
|
|
return await db.entityView.findMany({
|
|
where,
|
|
include: EntityViewModelHelper.includeDetails,
|
|
orderBy: {
|
|
order: "asc",
|
|
},
|
|
});
|
|
}
|
|
|
|
function buildEntityViewScopeWhere({
|
|
tenantId,
|
|
userId,
|
|
}: {
|
|
tenantId?: string | null;
|
|
userId?: string | null;
|
|
}): Prisma.EntityViewWhereInput {
|
|
if (tenantId === undefined) {
|
|
return {};
|
|
}
|
|
|
|
return {
|
|
OR: [
|
|
{ tenantId, userId },
|
|
{ tenantId, userId: null },
|
|
{ tenantId: null, userId: null },
|
|
],
|
|
};
|
|
}
|
|
|
|
export async function getEntityView(
|
|
id: string,
|
|
options?: {
|
|
entityId?: string;
|
|
tenantId?: string | null;
|
|
userId?: string | null;
|
|
}
|
|
): Promise<EntityViewWithDetails | null> {
|
|
return await db.entityView.findFirst({
|
|
where: {
|
|
id,
|
|
entityId: options?.entityId,
|
|
...buildEntityViewScopeWhere({ tenantId: options?.tenantId, userId: options?.userId }),
|
|
},
|
|
include: EntityViewModelHelper.includeDetails,
|
|
});
|
|
}
|
|
|
|
export async function getEntityViewWithTenantAndUser(id: string): Promise<EntityViewWithTenantAndUser | null> {
|
|
return await db.entityView.findUnique({
|
|
where: {
|
|
id,
|
|
},
|
|
include: {
|
|
...EntityViewModelHelper.includeDetails,
|
|
entity: { select: EntityModelHelper.selectSimpleProperties },
|
|
createdByUser: { select: UserModelHelper.selectSimpleUserProperties },
|
|
tenant: { select: TenantModelHelper.selectSimpleTenantProperties },
|
|
user: { select: UserModelHelper.selectSimpleUserProperties },
|
|
},
|
|
});
|
|
}
|
|
|
|
export async function getEntityViewByName({
|
|
entityId,
|
|
name,
|
|
isSystem,
|
|
tenantId,
|
|
userId,
|
|
}: {
|
|
entityId: string;
|
|
name: string;
|
|
isSystem?: boolean;
|
|
tenantId?: string | null;
|
|
userId?: string | null;
|
|
}): Promise<EntityViewWithDetails | null> {
|
|
return await db.entityView.findFirst({
|
|
where: {
|
|
entityId,
|
|
name,
|
|
isSystem,
|
|
...buildEntityViewScopeWhere({ tenantId, userId }),
|
|
},
|
|
include: EntityViewModelHelper.includeDetails,
|
|
});
|
|
}
|
|
|
|
export async function getEntityViewDefault({
|
|
entityId,
|
|
isSystem,
|
|
tenantId,
|
|
userId,
|
|
}: {
|
|
entityId: string;
|
|
isSystem?: boolean;
|
|
tenantId?: string | null;
|
|
userId?: string | null;
|
|
}): Promise<EntityViewWithDetails | null> {
|
|
return await db.entityView.findFirst({
|
|
where: {
|
|
entityId,
|
|
isSystem,
|
|
isDefault: true,
|
|
...buildEntityViewScopeWhere({ tenantId, userId }),
|
|
},
|
|
include: EntityViewModelHelper.includeDetails,
|
|
});
|
|
}
|
|
|
|
export async function getEntityViewByOrder(entityId: string, order: number): Promise<EntityViewWithDetails | null> {
|
|
return await db.entityView.findFirst({
|
|
where: {
|
|
entityId,
|
|
order,
|
|
},
|
|
include: EntityViewModelHelper.includeDetails,
|
|
});
|
|
}
|
|
|
|
export default async function getMaxEntityViewOrder(entityId: string): Promise<number> {
|
|
return (
|
|
(
|
|
await db.entityView.aggregate({
|
|
where: { entityId },
|
|
_max: {
|
|
order: true,
|
|
},
|
|
})
|
|
)._max?.order ?? 0
|
|
);
|
|
}
|
|
|
|
export async function createEntityView(data: {
|
|
entityId: string;
|
|
tenantId: string | null;
|
|
userId: string | null;
|
|
createdByUserId: string;
|
|
layout: string;
|
|
name: string;
|
|
title: string;
|
|
isDefault: boolean;
|
|
isSystem: boolean;
|
|
pageSize: number;
|
|
groupByPropertyId?: string | null;
|
|
gridColumns?: number;
|
|
gridColumnsSm?: number;
|
|
gridColumnsMd?: number;
|
|
gridColumnsLg?: number;
|
|
gridColumnsXl?: number;
|
|
gridColumns2xl?: number;
|
|
gridGap?: string;
|
|
order?: number;
|
|
}) {
|
|
if (!data.order) {
|
|
data.order = (await getMaxEntityViewOrder(data.entityId)) + 1;
|
|
}
|
|
return await db.entityView.create({
|
|
data: {
|
|
entityId: data.entityId,
|
|
tenantId: data.tenantId,
|
|
userId: data.userId,
|
|
createdByUserId: data.createdByUserId,
|
|
layout: data.layout,
|
|
order: data.order,
|
|
name: data.name,
|
|
title: data.title,
|
|
isDefault: data.isDefault,
|
|
isSystem: data.isSystem,
|
|
pageSize: data.pageSize,
|
|
groupByPropertyId: data.groupByPropertyId,
|
|
gridColumns: data.gridColumns ?? 5,
|
|
gridColumnsSm: data.gridColumnsSm ?? 2,
|
|
gridColumnsMd: data.gridColumnsMd ?? 3,
|
|
gridColumnsLg: data.gridColumnsLg ?? 4,
|
|
gridColumnsXl: data.gridColumnsXl ?? 5,
|
|
gridColumns2xl: data.gridColumns2xl ?? 6,
|
|
gridGap: data.gridGap ?? "sm",
|
|
},
|
|
});
|
|
}
|
|
|
|
export async function updateEntityViewProperties(id: string, items: { propertyId: string | null; name: string; order: number }[]) {
|
|
await db.entityViewProperty.deleteMany({
|
|
where: { entityViewId: id },
|
|
});
|
|
return await Promise.all(
|
|
items.map(async (item) => {
|
|
return await db.entityViewProperty.create({
|
|
data: {
|
|
entityViewId: id,
|
|
propertyId: item.propertyId,
|
|
name: item.name,
|
|
order: item.order,
|
|
},
|
|
});
|
|
})
|
|
);
|
|
}
|
|
|
|
type EntityViewScope = {
|
|
id: string;
|
|
entityId: string;
|
|
tenantId: string | null;
|
|
userId: string | null;
|
|
};
|
|
|
|
async function assertScopedEntityView(scope: EntityViewScope) {
|
|
const view = await getEntityView(scope.id, { entityId: scope.entityId, tenantId: scope.tenantId, userId: scope.userId });
|
|
if (!view || view.tenantId !== scope.tenantId || view.userId !== scope.userId) {
|
|
throw Error("Entity view not found");
|
|
}
|
|
return view;
|
|
}
|
|
|
|
export async function updateScopedEntityViewProperties(scope: EntityViewScope, items: { propertyId: string | null; name: string; order: number }[]) {
|
|
await assertScopedEntityView(scope);
|
|
return await updateEntityViewProperties(scope.id, items);
|
|
}
|
|
|
|
export async function updateEntityViewFilters(id: string, items: { name: string; condition: string; value: string; match: string }[]) {
|
|
await db.entityViewFilter.deleteMany({
|
|
where: { entityViewId: id },
|
|
});
|
|
return await Promise.all(
|
|
items.map(async (item) => {
|
|
return await db.entityViewFilter.create({
|
|
data: {
|
|
entityViewId: id,
|
|
name: item.name,
|
|
condition: item.condition,
|
|
value: item.value,
|
|
match: item.match,
|
|
},
|
|
});
|
|
})
|
|
);
|
|
}
|
|
|
|
export async function updateScopedEntityViewFilters(scope: EntityViewScope, items: { name: string; condition: string; value: string; match: string }[]) {
|
|
await assertScopedEntityView(scope);
|
|
return await updateEntityViewFilters(scope.id, items);
|
|
}
|
|
|
|
export async function updateEntityViewSort(id: string, items: { name: string; asc: boolean; order: number }[]) {
|
|
await db.entityViewSort.deleteMany({
|
|
where: { entityViewId: id },
|
|
});
|
|
return await Promise.all(
|
|
items.map(async (item) => {
|
|
return await db.entityViewSort.create({
|
|
data: {
|
|
entityViewId: id,
|
|
name: item.name,
|
|
asc: item.asc,
|
|
order: item.order,
|
|
},
|
|
});
|
|
})
|
|
);
|
|
}
|
|
|
|
export async function updateScopedEntityViewSort(scope: EntityViewScope, items: { name: string; asc: boolean; order: number }[]) {
|
|
await assertScopedEntityView(scope);
|
|
return await updateEntityViewSort(scope.id, items);
|
|
}
|
|
|
|
export async function updateEntityView(
|
|
id: string,
|
|
data: {
|
|
layout?: string;
|
|
isDefault?: boolean;
|
|
isSystem?: boolean;
|
|
pageSize?: number;
|
|
name?: string;
|
|
title?: string;
|
|
order?: number;
|
|
groupByPropertyId?: string | null;
|
|
gridColumns?: number;
|
|
gridColumnsSm?: number;
|
|
gridColumnsMd?: number;
|
|
gridColumnsLg?: number;
|
|
gridColumnsXl?: number;
|
|
gridColumns2xl?: number;
|
|
gridGap?: string;
|
|
}
|
|
) {
|
|
return await db.entityView.update({
|
|
where: { id },
|
|
data,
|
|
});
|
|
}
|
|
|
|
type UpdateEntityViewData = Parameters<typeof updateEntityView>[1];
|
|
|
|
export async function updateScopedEntityView(scope: EntityViewScope, data: UpdateEntityViewData) {
|
|
const result = await db.entityView.updateMany({
|
|
where: scope,
|
|
data,
|
|
});
|
|
if (result.count !== 1) {
|
|
throw Error("Entity view not found");
|
|
}
|
|
return await assertScopedEntityView(scope);
|
|
}
|
|
|
|
export async function deleteEntityView(id: string) {
|
|
return await db.entityView.delete({
|
|
where: { id },
|
|
});
|
|
}
|
|
|
|
export async function deleteScopedEntityView(scope: EntityViewScope) {
|
|
const result = await db.entityView.deleteMany({
|
|
where: scope,
|
|
});
|
|
if (result.count !== 1) {
|
|
throw Error("Entity view not found");
|
|
}
|
|
}
|