Files
HiveGrid-Wireframes/hivegrid-cloud/apps/web/app/utils/api/.server/EntityViewsApi.ts
T

359 lines
12 KiB
TypeScript

import { EntityView } from "@toolctrl/db/prisma-browser";
import { EntityWithDetails, getEntityByName } from "../../db/entities/entities.db.server";
import {
createEntityView,
EntityViewWithDetails,
getEntityView,
getEntityViewByName,
getEntityViewDefault,
getEntityViews,
updateEntityViewFilters,
updateEntityViewProperties,
updateEntityViewSort,
updateScopedEntityView,
updateScopedEntityViewFilters,
updateScopedEntityViewProperties,
updateScopedEntityViewSort,
} from "../../db/entities/entityViews.db.server";
import { RowsApi } from "./RowsApi";
import { getUser } from "../../db/users.db.server";
import { getTenant } from "../../db/tenants.db.server";
import { PropertyType } from "~/application/enums/entities/PropertyType";
export namespace EntityViewsApi {
export type GetEntityViewsWithRows = {
view: EntityViewWithDetails | undefined;
rowsData: RowsApi.GetRowsData;
rowsCount: number;
};
export async function getAll({
entityName,
tenantId,
withDefault = true,
withRows = true,
}: {
entityName: string;
tenantId: string | null;
withDefault: boolean;
withRows: boolean;
}): Promise<GetEntityViewsWithRows[]> {
const entity = await getEntityByName({ tenantId, name: entityName });
const views = await getEntityViews(entity.id, { tenantId });
let allViews: (EntityViewWithDetails | undefined)[] = views;
if (withDefault) {
allViews = [undefined, ...views];
}
return await Promise.all(
allViews.map(async (entityView) => {
let rowsData = await RowsApi.getAll({
entity,
tenantId,
entityView,
urlSearchParams: new URLSearchParams(),
pageSize: 10000,
});
const rowsCount = rowsData.items.length;
if (!withRows) {
rowsData = { ...rowsData, items: [] };
}
const data: GetEntityViewsWithRows = {
view: entityView,
rowsData,
rowsCount,
};
return data;
})
);
}
export async function get(
id: string | undefined,
{ entityName, tenantId, pageSize }: { entityName: string; tenantId: string | null; pageSize?: number }
): Promise<GetEntityViewsWithRows | null> {
const entity = await getEntityByName({ tenantId, name: entityName });
if (!entity) {
return null;
}
let view: EntityViewWithDetails | null = null;
if (id) {
view = await getEntityView(id, { entityId: entity.id, tenantId });
if (!view) {
return null;
}
}
const rowsData = await RowsApi.getAll({
entity,
tenantId,
entityView: view ?? undefined,
urlSearchParams: new URLSearchParams(),
pageSize,
});
const data: GetEntityViewsWithRows = {
view: view ?? undefined,
rowsData,
rowsCount: rowsData.items.length,
};
return data;
}
export async function createFromForm({ entity, form, createdByUserId }: { form: FormData; entity: EntityWithDetails; createdByUserId: string }) {
const layout = form.get("layout")?.toString() ?? "";
const name = form.get("name")?.toString().toLowerCase() ?? "";
const title = form.get("title")?.toString() ?? "";
const pageSize = Number(form.get("pageSize"));
const isDefault = Boolean(form.get("isDefault"));
// Board
const groupBy = form.get("groupBy")?.toString() ?? undefined;
const groupByPropertyId = form.get("groupByPropertyId")?.toString() ?? undefined;
// Grid
const gridColumns = Number(form.get("gridColumns") ?? 0);
const gridColumnsSm = Number(form.get("gridColumnsSm") ?? 0);
const gridColumnsMd = Number(form.get("gridColumnsMd") ?? 0);
const gridColumnsLg = Number(form.get("gridColumnsLg") ?? 0);
const gridColumnsXl = Number(form.get("gridColumnsXl") ?? 0);
const gridColumns2xl = Number(form.get("gridColumns2xl") ?? 0);
const gridGap = form.get("gridGap")?.toString() ?? "sm";
const isSystem = form.get("isSystem") === "true";
let tenantId: string | null = form.get("tenantId")?.toString() ?? null;
let userId: string | null = form.get("userId")?.toString() ?? null;
if (!tenantId?.toString().trim()) {
tenantId = null;
}
if (!userId?.toString().trim()) {
userId = null;
}
const errors = [
...(await validateEntityView({ entityId: entity.id, isDefault, name, title, order: null, tenantId, userId })),
...(await validateGroupBy(entity, layout, groupBy, groupByPropertyId)),
];
if (errors.length > 0) {
throw Error(errors.join(", "));
}
const properties: { propertyId: string; name: string; order: number }[] = form.getAll("properties[]").map((f: FormDataEntryValue) => {
return JSON.parse(f.toString());
});
if (properties.length === 0) {
throw Error("Add at least one property to display");
}
const filters: { name: string; condition: string; value: string; match: string }[] = form.getAll("filters[]").map((f: FormDataEntryValue) => {
return JSON.parse(f.toString());
});
const sort: { name: string; asc: boolean; order: number }[] = form.getAll("sort[]").map((f: FormDataEntryValue) => {
return JSON.parse(f.toString());
});
if (tenantId) {
const tenant = await getTenant(tenantId);
if (!tenant) {
// eslint-disable-next-line no-console
console.log("Invalid account", { tenantId });
throw Error("Invalid account");
}
}
if (userId) {
const user = await getUser(userId);
if (!user) {
// eslint-disable-next-line no-console
console.log("Invalid user", { userId });
throw Error("Invalid user");
}
}
try {
const entityView = await createEntityView({
entityId: entity.id,
tenantId: tenantId,
userId: userId,
createdByUserId,
layout,
name,
title,
isDefault,
pageSize,
groupByPropertyId: groupBy === "byProperty" ? groupByPropertyId ?? null : null,
gridColumns,
gridColumnsSm,
gridColumnsMd,
gridColumnsLg,
gridColumnsXl,
gridColumns2xl,
gridGap,
isSystem,
});
await updateEntityViewProperties(entityView.id, properties);
await updateEntityViewFilters(entityView.id, filters);
await updateEntityViewSort(entityView.id, sort);
return entityView;
} catch (e: any) {
throw Error(e.message);
}
}
export async function updateFromForm({ item, entity, form }: { item: EntityView; form: FormData; entity: EntityWithDetails }) {
const layout = form.get("layout")?.toString() ?? "";
const name = form.get("name")?.toString() ?? "";
const title = form.get("title")?.toString() ?? "";
const pageSize = Number(form.get("pageSize"));
const order = Number(form.get("order"));
const isDefault = Boolean(form.get("isDefault"));
// Board
const groupBy = form.get("groupBy")?.toString() ?? undefined;
const groupByPropertyId = form.get("groupByPropertyId")?.toString() ?? undefined;
// Grid
const gridColumns = Number(form.get("gridColumns") ?? 0);
const gridColumnsSm = Number(form.get("gridColumnsSm") ?? 0);
const gridColumnsMd = Number(form.get("gridColumnsMd") ?? 0);
const gridColumnsLg = Number(form.get("gridColumnsLg") ?? 0);
const gridColumnsXl = Number(form.get("gridColumnsXl") ?? 0);
const gridColumns2xl = Number(form.get("gridColumns2xl") ?? 0);
const gridGap = form.get("gridGap")?.toString() ?? "sm";
const errors = [
...(await validateEntityView({ entityId: entity.id, isDefault, name, title, order, entityView: item, tenantId: item.tenantId, userId: item.userId })),
...(await validateGroupBy(entity, layout, groupBy, groupByPropertyId)),
];
if (errors.length > 0) {
throw Error(errors.join(", "));
}
const properties: { propertyId: string; name: string; order: number }[] = form.getAll("properties[]").map((f: FormDataEntryValue) => {
return JSON.parse(f.toString());
});
if (properties.length === 0) {
throw Error("Add at least one property to display");
}
const filters: { name: string; condition: string; value: string; match: string }[] = form.getAll("filters[]").map((f: FormDataEntryValue) => {
return JSON.parse(f.toString());
});
const sort: { name: string; asc: boolean; order: number }[] = form.getAll("sort[]").map((f: FormDataEntryValue) => {
return JSON.parse(f.toString());
});
try {
const viewScope = {
id: item.id,
entityId: entity.id,
tenantId: item.tenantId,
userId: item.userId,
};
const view = await updateScopedEntityView(viewScope, {
order,
layout,
name,
title,
isDefault,
pageSize,
groupByPropertyId: groupBy === "byProperty" ? groupByPropertyId ?? null : null,
gridColumns,
gridColumnsSm,
gridColumnsMd,
gridColumnsLg,
gridColumnsXl,
gridColumns2xl,
gridGap,
});
await updateScopedEntityViewProperties(viewScope, properties);
await updateScopedEntityViewFilters(viewScope, filters);
await updateScopedEntityViewSort(viewScope, sort);
return view;
} catch (e: any) {
throw Error(e.message);
}
}
export const getCurrentEntityView = async (
entityId: string,
urlParams: URLSearchParams,
{ tenantId, userId }: { tenantId?: string | null; userId?: string | null } = {}
) => {
const name = urlParams.get("v");
if (name) {
return await getEntityViewByName({ entityId, name, isSystem: false, tenantId, userId });
} else {
return await getEntityViewDefault({ entityId, isSystem: false, tenantId, userId });
}
};
const validateEntityView = async ({
entityId,
isDefault,
name,
title,
order,
entityView,
tenantId,
userId,
}: {
entityId: string;
isDefault: boolean;
name: string;
title: string;
order: number | null;
entityView?: EntityView;
tenantId: string | null;
userId: string | null;
}) => {
const errors: string[] = [];
const views = await getEntityViews(entityId, { tenantId, userId });
if (isDefault && userId) {
const defaultView = views.find((f) => f.id !== entityView?.id && f.isDefault && f.userId === userId);
if (defaultView) {
errors.push(`Existing default entity view with title: ${defaultView.title}`);
}
}
if (!entityView || entityView?.name !== name) {
const existingName = views.find((f) => f.id !== entityView?.id && f.name.toLowerCase() === name.toLowerCase());
if (existingName) {
errors.push(`Existing entity view with name: ${existingName.name}`);
}
}
if (!entityView || entityView?.title !== title) {
const existingTitle = views.find((f) => f.id !== entityView?.id && f.title.toLowerCase() === title.toLowerCase());
if (existingTitle) {
errors.push(`Existing entity view with title: ${existingTitle.title}`);
}
}
// if (order) {
// if (!entityView || entityView?.order !== order) {
// const existingOrder = await getEntityViewByOrder(entityId, order);
// if (existingOrder) {
// errors.push(`Existing entity view with order '${order}': ${existingOrder.title}`);
// }
// }
// }
return errors;
};
const validateGroupBy = async (entity: EntityWithDetails, layout: string, groupBy: string | undefined, groupByPropertyId: string | undefined) => {
if (layout !== "board") {
return [];
}
const errors: string[] = [];
if (groupBy === "byProperty") {
const property = entity.properties.find((p) => p.id === groupByPropertyId);
if (!property || property.type !== PropertyType.SELECT) {
errors.push(`Cannot save a Board view without a SELECT property`);
}
}
return errors;
};
}