diff --git a/.github/workflows/e2e.yml b/.github/workflows/e2e.yml
index 88e8714b..e6f629a4 100644
--- a/.github/workflows/e2e.yml
+++ b/.github/workflows/e2e.yml
@@ -35,7 +35,7 @@ jobs:
- name: Run tests
run: |
yarn turbo test:e2e
- BASE_URL=http://localhost:3000/legacy/admin yarn test:e2e
+ BASE_URL=http://localhost:3000/pagerouter/admin yarn test:e2e
- uses: actions/upload-artifact@v3
if: always()
with:
diff --git a/apps/docs/pages/docs/api-docs.mdx b/apps/docs/pages/docs/api-docs.mdx
index da4a0582..a008d235 100644
--- a/apps/docs/pages/docs/api-docs.mdx
+++ b/apps/docs/pages/docs/api-docs.mdx
@@ -1,86 +1,140 @@
+import { Tabs } from "nextra/components";
+
# API
-## `nextAdminRouter` function
+## Functions
-`nextAdminRouter` is a function that returns a promise of a _Node Router_ that you can use in your getServerSideProps function to start using Next Admin. Its usage is only related to Page router.
+
+
+ The following is used only for App router.
-Usage example:
+ ## `getPropsFromParams` function
-```ts
-// pages/api/admin/[[...nextadmin]].ts
-export const getServerSideProps: GetServerSideProps = async ({ req, res }) => {
- const { nextAdminRouter } = await import(
- "@premieroctet/next-admin/dist/router"
- );
- const adminRouter = await nextAdminRouter(prisma, schema);
- return adminRouter.run(req, res) as Promise<
- GetServerSidePropsResult<{ [key: string]: any }>
- >;
-};
-```
+ `getPropsFromParams` is a function that returns the props for the [`NextAdmin`](#nextadmin--component) component. It accepts one argument which is an object with the following properties:
-It takes 3 parameters:
+ - `params`: the array of route params retrieved from the [optional catch-all segment](https://nextjs.org/docs/app/building-your-application/routing/dynamic-routes#optional-catch-all-segments)
+ - `searchParams`: the query params [retrieved from the page](https://nextjs.org/docs/app/api-reference/file-conventions/page#searchparams-optional)
+ - `options`: the [options](#next-admin-options) object
+ - `schema`: the json schema generated by the `prisma generate` command
+ - `prisma`: your Prisma client instance
+ - `action`: the [server action](https://nextjs.org/docs/app/api-reference/functions/server-actions) used to submit the form. It should be your own action, that wraps the `submitForm` action imported from `@premieroctet/next-admin/dist/actions`.
-- Your Prisma client instance, _required_
-- Your Prisma schema, _required_
+
+
+ The following is used only for Page router
-and an _optional_ object of type [`NextAdminOptions`](#next-admin-options) to customize your admin with the following properties:
+ ## `nextAdminRouter` function
-```ts
-import { NextAdminOptions } from "@premieroctet/next-admin";
+ `nextAdminRouter` is a function that returns a promise of a _Node Router_ that you can use in your getServerSideProps function to start using Next Admin. Its usage is only related to Page router.
-const options: NextAdminOptions = {
- model: {
- User: {
- toString: (user) => `${user.email} / ${user.name}`,
- },
- },
-};
+ Usage example:
-const adminRouter = await nextAdminRouter(prisma, schema, options);
-```
+ ```ts
+ // pages/api/admin/[[...nextadmin]].ts
+ export const getServerSideProps: GetServerSideProps = async ({ req, res }) => {
+ const { nextAdminRouter } = await import(
+ "@premieroctet/next-admin/dist/router"
+ );
+ const adminRouter = await nextAdminRouter(prisma, schema);
+ return adminRouter.run(req, res) as Promise<
+ GetServerSidePropsResult<{ [key: string]: any }>
+ >;
+ };
+ ```
-## `getPropsFromParams` function
+ It takes 3 parameters:
-`getPropsFromParams` is a function that returns the props for the [`NextAdmin`](#nextadmin--component) component. It accepts one argument which is an object with the following properties:
+ - Your Prisma client instance, _required_
+ - Your Prisma schema, _required_
-- `params`: the array of route params retrieved from the [optional catch-all segment](https://nextjs.org/docs/app/building-your-application/routing/dynamic-routes#optional-catch-all-segments)
-- `searchParams`: the query params [retrieved from the page](https://nextjs.org/docs/app/api-reference/file-conventions/page#searchparams-optional)
-- `options`: the [options](#next-admin-options) object
-- `schema`: the json schema generated by the `prisma generate` command
-- `prisma`: your Prisma client instance
-- `action`: the [server action](https://nextjs.org/docs/app/api-reference/functions/server-actions) used to submit the form. It should be your own action, that wraps the `submitForm` action imported from `@premieroctet/next-admin/dist/actions`.
+ and an _optional_ object of type [`NextAdminOptions`](#next-admin-options) to customize your admin with the following properties:
-## Authentication
+ ```ts
+ import { NextAdminOptions } from "@premieroctet/next-admin";
-The library does not provide an authentication system. If you want to add your own, you can do so by adding a role check to the `getServerSideProps` function:
+ const options: NextAdminOptions = {
+ model: {
+ User: {
+ toString: (user) => `${user.email} / ${user.name}`,
+ },
+ },
+ };
-> The following example uses [next-auth](https://next-auth.js.org/) to handle authentication
+ const adminRouter = await nextAdminRouter(prisma, schema, options);
+ ```
-```ts
-// pages/api/admin/[[...nextadmin]].ts
+
+
-export const getServerSideProps: GetServerSideProps = async ({ req, res }) => {
- const session = await getServerSession(req, res, authOptions);
- const isAdmin = session?.user?.role === "SUPERADMIN"; // your role check
+## Authentication
- if (!isAdmin) {
- return {
- redirect: {
- destination: "/",
- permanent: false,
- },
- };
- }
+
+
+ The library does not provide an authentication system. If you want to add your own, you can do so by adding a role check in the page:
+
+ > The following example uses [next-auth](https://next-auth.js.org/) to handle authentication
+
+ ```ts
+ // app/admin/[[...nextadmin]]/page.tsx
+
+ export default async function AdminPage({
+ params,
+ searchParams,
+ }: {
+ params: { [key: string]: string[] };
+ searchParams: { [key: string]: string | string[] | undefined } | undefined;
+ }) {
+ const session = await getServerSession(authOptions);
+ const isAdmin = session?.user?.role === "SUPERADMIN"; // your role check
+
+ if (!isAdmin) {
+ redirect('/', { permanent: false })
+ }
+
+ const props = await getPropsFromParams({
+ params: params.nextadmin,
+ searchParams,
+ options,
+ prisma,
+ schema,
+ action: submitFormAction,
+ });
+
+ return ;
+ }
+ ```
+
+
+
+ The library does not provide an authentication system. If you want to add your own, you can do so by adding a role check to the `getServerSideProps` function:
+
+ > The following example uses [next-auth](https://next-auth.js.org/) to handle authentication
+
+ ```ts
+ // pages/api/admin/[[...nextadmin]].ts
+
+ export const getServerSideProps: GetServerSideProps = async ({ req, res }) => {
+ const session = await getServerSession(req, res, authOptions);
+ const isAdmin = session?.user?.role === "SUPERADMIN"; // your role check
+
+ if (!isAdmin) {
+ return {
+ redirect: {
+ destination: "/",
+ permanent: false,
+ },
+ };
+ }
- const { nextAdminRouter } = await import(
- "@premieroctet/next-admin/dist/nextAdminRouter"
- );
- return nextAdminRouter(client).run(req, res);
-};
-```
+ const { nextAdminRouter } = await import(
+ "@premieroctet/next-admin/dist/nextAdminRouter"
+ );
+ return nextAdminRouter(client).run(req, res);
+ };
+ ```
-For App router, this check will happen directly in your page.
+
+
## `` component
diff --git a/apps/example/pages/legacy/[[...nextadmin]].tsx b/apps/example/pages/pagerouter/[[...nextadmin]].tsx
similarity index 96%
rename from apps/example/pages/legacy/[[...nextadmin]].tsx
rename to apps/example/pages/pagerouter/[[...nextadmin]].tsx
index 3140ad0f..185a01c2 100644
--- a/apps/example/pages/legacy/[[...nextadmin]].tsx
+++ b/apps/example/pages/pagerouter/[[...nextadmin]].tsx
@@ -8,7 +8,7 @@ import schema from "../../prisma/json-schema/json-schema.json";
const pageOptions = {
...options,
- basePath: "/legacy/admin",
+ basePath: "/pagerouter/admin",
};
export default function Admin(props: AdminComponentProps) {
diff --git a/packages/next-admin/src/components/Form.tsx b/packages/next-admin/src/components/Form.tsx
index 5aa01dfd..c790f687 100644
--- a/packages/next-admin/src/components/Form.tsx
+++ b/packages/next-admin/src/components/Form.tsx
@@ -170,10 +170,6 @@ const Form = ({
);
};
- useEffect(() => {
- setValidation(validationProp);
- }, [validationProp]);
-
const extraErrors: ErrorSchema | undefined = validation?.reduce(
(acc, curr) => {
// @ts-expect-error
diff --git a/packages/next-admin/src/utils/actions.ts b/packages/next-admin/src/utils/actions.ts
index 8ffa966a..03bb1709 100644
--- a/packages/next-admin/src/utils/actions.ts
+++ b/packages/next-admin/src/utils/actions.ts
@@ -1,5 +1,10 @@
import { ActionParams } from "../types";
+/**
+ * Following https://nextjs.org/docs/app/api-reference/functions/server-actions#binding-arguments
+ * We need the params and schema options to be there when the action is called.
+ * Other params (prisma, options) will be added by the app's action implementation.
+ */
export const createBoundServerAction = (
{ params, schema }: ActionParams,
action: (params: ActionParams, formData: FormData) => Promise
diff --git a/packages/next-admin/src/utils/server.test.ts b/packages/next-admin/src/utils/server.test.ts
new file mode 100644
index 00000000..2aa32b49
--- /dev/null
+++ b/packages/next-admin/src/utils/server.test.ts
@@ -0,0 +1,66 @@
+import {
+ getResourceFromParams,
+ getResourceFromUrl,
+ getResourceIdFromParam,
+ getResourceIdFromUrl,
+} from "./server";
+
+describe("Server utils", () => {
+ describe("getResourceFromUrl", () => {
+ it("should return a resource with /api/User", () => {
+ expect(getResourceFromUrl("/api/User", ["User"])).toEqual("User");
+ });
+
+ it("should return a resource with /api/User/1", () => {
+ expect(getResourceFromUrl("/api/User/1", ["User"])).toEqual("User");
+ });
+
+ it("should not return a resource with /api/Post", () => {
+ expect(getResourceFromUrl("/api/Post", ["User"])).toEqual(undefined);
+ });
+ });
+
+ describe("getResourceFromParams", () => {
+ it("should return a resource with /api/User", () => {
+ expect(getResourceFromParams(["User"], ["User"])).toEqual("User");
+ });
+
+ it("should return a resource with /api/User/1", () => {
+ expect(getResourceFromParams(["User", "1"], ["User"])).toEqual("User");
+ });
+
+ it("should not return a resource with /api/Post", () => {
+ expect(getResourceFromParams(["Post"], ["User"])).toEqual(undefined);
+ });
+ });
+
+ describe("getResourceIdFromUrl", () => {
+ it("should get the id from /api/User/1", () => {
+ expect(getResourceIdFromUrl("/api/User/1", "User")).toEqual(1);
+ });
+
+ it("should not return an id from /api/User/new", () => {
+ expect(getResourceIdFromUrl("/api/User/new", "User")).toEqual(undefined);
+ });
+
+ it("should not return an id from /api/Dummy/--__", () => {
+ expect(getResourceIdFromUrl("/api/Dummy/--__", "User")).toEqual(
+ undefined
+ );
+ });
+ });
+
+ describe("getResourceIdFromParam", () => {
+ it("should get the id from /api/User/1", () => {
+ expect(getResourceIdFromParam("1", "User")).toEqual(1);
+ });
+
+ it("should not return an id from /api/User/new", () => {
+ expect(getResourceIdFromParam("new", "User")).toEqual(undefined);
+ });
+
+ it("should not return an id from /api/Dummy/--__", () => {
+ expect(getResourceIdFromParam("--__", "User")).toEqual(NaN);
+ });
+ });
+});