Skip to content

Commit

Permalink
Merge branch 'main' into feat/generator-custom
Browse files Browse the repository at this point in the history
  • Loading branch information
cregourd committed Oct 9, 2024
2 parents 2aa241a + 0d2b092 commit 02ae70a
Show file tree
Hide file tree
Showing 13 changed files with 133 additions and 34 deletions.
8 changes: 8 additions & 0 deletions apps/docs/pages/changelog/index.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,13 @@
# @premieroctet/next-admin

## 6.1.6

### Patch Changes

- [56ea03b](https://github.com/premieroctet/next-admin/commit/56ea03b): feat: add depth selection for actions ([#443](https://github.com/premieroctet/next-admin/issues/443))
- [81b2e54](https://github.com/premieroctet/next-admin/commit/81b2e54): Fix relation one-to-many - nullable relation
- [3225788](https://github.com/premieroctet/next-admin/commit/3225788): Fix image (get async)

## 6.1.5

### Patch Changes
Expand Down
6 changes: 6 additions & 0 deletions apps/docs/pages/docs/api/model-configuration.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -652,6 +652,12 @@ The `actions` property is an array of objects that allows you to define a set of
description:
"a message that will be displayed when the action fails if action doesn't return a message object or throw an error with a message",
},
{
name: "depth",
type: "Number",
description:
"a number that defines the depth of the relations to select in the resource. Use this with caution, a number too high can potentially cause slower queries. Defaults to 2.",
},
]}
/>

Expand Down
3 changes: 2 additions & 1 deletion apps/example/options.tsx
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import UserDetailsDialog from "@/components/UserDetailsDialogContent";
import AddTagDialog from "@/components/PostAddTagDialogContent";
import UserDetailsDialog from "@/components/UserDetailsDialogContent";
import { NextAdminOptions } from "@premieroctet/next-admin";
import DatePicker from "./components/DatePicker";
import PasswordInput from "./components/PasswordInput";
Expand Down Expand Up @@ -189,6 +189,7 @@ export const options: NextAdminOptions = {
id: "user-details",
title: "actions.user.details.title",
component: <UserDetailsDialog />,
depth: 3,
},
],
},
Expand Down
7 changes: 7 additions & 0 deletions packages/next-admin/CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,13 @@
### Major Changes

- [1fa56bc](https://github.com/premieroctet/next-admin/commit/1fa56bc): feat: add custom generator ([#414](https://github.com/premieroctet/next-admin/issues/414))
## 6.1.6

### Patch Changes

- [56ea03b](https://github.com/premieroctet/next-admin/commit/56ea03b): feat: add depth selection for actions ([#443](https://github.com/premieroctet/next-admin/issues/443))
- [81b2e54](https://github.com/premieroctet/next-admin/commit/81b2e54): Fix relation one-to-many - nullable relation
- [3225788](https://github.com/premieroctet/next-admin/commit/3225788): Fix image (get async)

## 6.1.5

Expand Down
16 changes: 15 additions & 1 deletion packages/next-admin/src/appHandler.ts
Original file line number Diff line number Diff line change
Expand Up @@ -58,11 +58,25 @@ export const createHandler = <P extends string = "nextadmin">({
?.split(",")
.map((id) => formatId(resource, id));

const depth = req.nextUrl.searchParams.get("depth");

if (!ids) {
return NextResponse.json({ error: "No ids provided" }, { status: 400 });
}

const data = await getRawData({ prisma, resource, resourceIds: ids });
if (depth && isNaN(Number(depth))) {
return NextResponse.json(
{ error: "Depth should be a number" },
{ status: 400 }
);
}

const data = await getRawData({
prisma,
resource,
resourceIds: ids,
maxDepth: depth ? Number(depth) : undefined,
});

return NextResponse.json(data);
})
Expand Down
13 changes: 10 additions & 3 deletions packages/next-admin/src/components/ClientActionDialog.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -42,9 +42,16 @@ const ClientActionDialog = <M extends ModelName>({

useEffect(() => {
setIsLoading(true);
fetch(
`${apiBasePath}/${slugify(resource)}/raw?ids=${resourceIds.join(",")}`
)
const params = new URLSearchParams();

params.set("ids", resourceIds.join(","));

if (action.depth) {
// Avoid negative depth
params.set("depth", Math.max(1, action.depth).toString());
}

fetch(`${apiBasePath}/${slugify(resource)}/raw?${params.toString()}`)
.then((res) => res.json())
.then(setData)
.finally(() => {
Expand Down
18 changes: 14 additions & 4 deletions packages/next-admin/src/components/inputs/FileWidget.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -9,12 +9,12 @@ import { ChangeEvent, useEffect, useRef, useState } from "react";
import Loader from "../../assets/icons/Loader";
import { useFormState } from "../../context/FormStateContext";
import { useI18n } from "../../context/I18nContext";
import { getFilenameFromUrl, isImageType } from "../../utils/file";
import { getFilenameFromUrl } from "../../utils/file";

const FileWidget = (props: WidgetProps) => {
const [file, setFile] = useState<File>();
const [errors, setErrors] = useState(props.rawErrors);
const [fileIsImage, setFileIsImage] = useState(false);
const [fileIsImage, setFileIsImage] = useState(true);
const [filename, setFilename] = useState<string | null>(null);
const [fileUrl, setFileUrl] = useState<string | null>(props.value);
const [isPending, setIsPending] = useState(false);
Expand All @@ -35,12 +35,22 @@ const FileWidget = (props: WidgetProps) => {
useEffect(() => {
if (props.value) {
setIsPending(true);
setFileUrl(props.value);

const image = document.createElement("img");
image.src = props.value as string;
image.onload = () => {
setFileIsImage(true);
setIsPending(false);
};
image.onerror = (e) => {
console.error(e);
setFileIsImage(false);
setIsPending(false);
};
const filename = getFilenameFromUrl(props.value);
if (filename) {
setFilename(filename);
}
setFileIsImage(isImageType(props.value));
setIsPending(false);
} else {
setIsPending(false);
Expand Down
4 changes: 2 additions & 2 deletions packages/next-admin/src/components/inputs/SelectWidget.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -36,7 +36,7 @@ const SelectWidget = ({

const { basePath } = useConfig();

const handleChange = (option: Enumeration) => {
const handleChange = (option: Enumeration | null) => {
setFieldDirty(props.name);
onChange(option);
onClose();
Expand Down Expand Up @@ -95,7 +95,7 @@ const SelectWidget = ({
className="flex items-center"
onClick={(e) => {
e.preventDefault();
onChange({});
handleChange(null);
}}
>
<XMarkIcon className="h-5 w-5 cursor-pointer text-gray-400" />
Expand Down
13 changes: 12 additions & 1 deletion packages/next-admin/src/pageHandler.ts
Original file line number Diff line number Diff line change
Expand Up @@ -86,7 +86,18 @@ export const createHandler = <P extends string = "nextadmin">({
ids = ids?.split(",").map((id: string) => formatId(resource, id));
}

const data = await getRawData({ prisma, resource, resourceIds: ids });
const depth = req.query.depth;

if (depth && isNaN(Number(depth))) {
return res.status(400).json({ error: "Depth should be a number" });
}

const data = await getRawData({
prisma,
resource,
resourceIds: ids,
maxDepth: depth ? Number(depth) : undefined,
});

return res.json(data);
})
Expand Down
6 changes: 6 additions & 0 deletions packages/next-admin/src/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -549,6 +549,12 @@ export type BareModelAction<M extends ModelName> = {
canExecute?: (item: Model<M>) => boolean;
icon?: keyof typeof OutlineIcons;
style?: ActionStyle;
/**
* Max depth of the related records to select
*
* @default 2
*/
depth?: number;
};

export type ServerAction = {
Expand Down
56 changes: 40 additions & 16 deletions packages/next-admin/src/utils/prisma.ts
Original file line number Diff line number Diff line change
@@ -1,9 +1,6 @@
import { $Enums, Prisma, PrismaClient } from "@prisma/client";
import type { NextAdminJsonSchemaData } from "@premieroctet/next-admin-json-schema";
import { Prisma, PrismaClient } from "@prisma/client";
import { cloneDeep } from "lodash";
import type {
NextAdminJSONSchema,
NextAdminJsonSchemaData,
} from "@premieroctet/next-admin-json-schema";
import { ITEMS_PER_PAGE } from "../config";
import {
EditOptions,
Expand All @@ -22,17 +19,17 @@ import {
Select,
} from "../types";
import { validateQuery } from "./advancedSearch";
import { getDefinitionFromRef } from "./jsonSchema";
import {
enumValueForEnumType,
findRelationInData,
getModelIdProperty,
getToStringForRelations,
modelHasIdField,
globalSchema,
modelHasIdField,
transformData,
} from "./server";
import { capitalize, isScalar, uncapitalize } from "./tools";
import { getDefinitionFromRef } from "./jsonSchema";

type CreateNestedWherePredicateParams<M extends ModelName> = {
field: NextAdminJsonSchemaData & { name: string };
Expand Down Expand Up @@ -702,6 +699,39 @@ export const getDataItem = async <M extends ModelName>({
return data;
};

type DeepIncludeRecord = Record<string, true | any>;

const includeDataByDepth = <M extends ModelName>(
modelProperties: SchemaDefinitions[ModelName]["properties"],
currentDepth: number,
maxDepth: number
) => {
const include = Object.entries(modelProperties)?.reduce(
(acc, [name, field]) => {
if (field.__nextadmin?.kind === "object") {
/**
* We substract because, if the condition matches,
* we will have all the fields in the related model, which are
* counted in currentDepth + 1
*/
if (currentDepth < maxDepth - 1) {
const nextModel =
globalSchema.definitions[field.__nextadmin.type as M].properties;
acc[name] = {
include: includeDataByDepth(nextModel, currentDepth + 1, maxDepth),
};
} else {
acc[name] = true;
}
}
return acc;
},
{} as DeepIncludeRecord
);

return include;
};

/**
* Get raw data from Prisma (2-deep nested relations)
* @param prisma
Expand All @@ -713,25 +743,19 @@ export const getRawData = async <M extends ModelName>({
prisma,
resource,
resourceIds,
maxDepth = 2,
}: {
prisma: PrismaClient;
resource: M;
resourceIds: Array<string | number>;
maxDepth?: number;
}): Promise<Model<M>[]> => {
const model = globalSchema.definitions[
resource
] as SchemaDefinitions[ModelName];
const modelProperties = model.properties;

const include = Object.entries(modelProperties).reduce(
(acc, [name, field]) => {
if (field.__nextadmin?.kind === "object") {
acc[name] = true;
}
return acc;
},
{} as Record<string, true>
);
const include = includeDataByDepth(modelProperties!, 1, maxDepth);

// @ts-expect-error
const data = await prisma[resource].findMany({
Expand Down
11 changes: 8 additions & 3 deletions packages/next-admin/src/utils/server.ts
Original file line number Diff line number Diff line change
Expand Up @@ -65,10 +65,14 @@ export const getEnableToExecuteActions = async <M extends ModelName>(
actions?: Omit<ModelAction<M>, "action">[]
): Promise<OutputModelAction | undefined> => {
if (actions?.some((action) => action.canExecute)) {
const maxDepth = Math.max(0, ...actions.map((action) => action.depth ?? 0));

const data: Model<typeof resource>[] = await getRawData<typeof resource>({
prisma,
resource,
resourceIds: ids,
// Apply the default value if its 0
maxDepth: maxDepth || undefined,
});

return actions?.reduce(
Expand Down Expand Up @@ -348,7 +352,8 @@ export const transformData = <M extends ModelName>(

const schemaProperties = model.properties;

return Object.keys(data).reduce((acc, key) => {
return Object.keys(data).reduce(async (accP, key) => {
const acc = await accP;
const field = schemaProperties[key as keyof typeof schemaProperties];
const fieldKind = field?.__nextadmin?.kind;
const get = editOptions?.fields?.[key as Field<M>]?.handler?.get;
Expand All @@ -357,7 +362,7 @@ export const transformData = <M extends ModelName>(
editOptions?.fields?.[key as Field<M>]?.relationshipSearchField;

if (get) {
acc[key] = get(data[key]);
acc[key] = await get(data[key]);
} else if (fieldKind === "enum") {
const value = data[key];
if (Array.isArray(value)) {
Expand Down Expand Up @@ -450,7 +455,7 @@ export const transformData = <M extends ModelName>(
}
}
return acc;
}, {} as any);
}, Promise.resolve({}) as any);
};

/**
Expand Down
6 changes: 3 additions & 3 deletions yarn.lock
Original file line number Diff line number Diff line change
Expand Up @@ -2759,7 +2759,7 @@ __metadata:
languageName: unknown
linkType: soft

"@premieroctet/next-admin@npm:6.1.5, @premieroctet/next-admin@workspace:packages/next-admin":
"@premieroctet/next-admin@npm:7.0.0-rc.0, @premieroctet/next-admin@workspace:packages/next-admin":
version: 0.0.0-use.local
resolution: "@premieroctet/next-admin@workspace:packages/next-admin"
dependencies:
Expand Down Expand Up @@ -7075,7 +7075,7 @@ __metadata:
dependencies:
"@babel/core": "npm:^7.0.0"
"@heroicons/react": "npm:^2.1.1"
"@premieroctet/next-admin": "npm:6.1.5"
"@premieroctet/next-admin": "npm:7.0.0-rc.0"
"@types/node": "npm:^17.0.12"
"@types/react": "npm:^18.2.0"
"@types/react-dom": "npm:^18.2.0"
Expand Down Expand Up @@ -8138,7 +8138,7 @@ __metadata:
"@heroicons/react": "npm:^2.0.18"
"@picocss/pico": "npm:^1.5.7"
"@playwright/test": "npm:^1.37.0"
"@premieroctet/next-admin": "npm:6.1.5"
"@premieroctet/next-admin": "npm:7.0.0-rc.0"
"@premieroctet/next-admin-generator-prisma": "workspace:*"
"@prisma/client": "npm:5.14.0"
"@tremor/react": "npm:^3.2.2"
Expand Down

0 comments on commit 02ae70a

Please sign in to comment.