premieroctet / next-admin Goto Github PK
View Code? Open in Web Editor NEW▲ Full-featured admin for Next.js and Prisma
Home Page: https://next-admin.js.org
License: MIT License
▲ Full-featured admin for Next.js and Prisma
Home Page: https://next-admin.js.org
License: MIT License
Hi, thanks for this awesome library!
Would it be possible to have an option to only export the data table so that it can be integrated with existing UIs a bit nicer? I'm interested in using this for an application that has an admin view as well as a normal view, but this would require the normal view to look identical to the styling of this library (ie: sidebar, logo, colors), and it would be amazing if we can directly place data tables in existing UIs.
How about to implement a new option to make the form layout responsive, you could use grid and add tailwind breakpoints to set number of columns in the grid, example:
edit: {
gridColumns: {
sm: 1, // 640px
md: 2, // 768px
lg: 3, // 1024px
xl: 4, // 1280px
},
display: ["field1", "field2", "field_n"]
}
To reduce core dependencies, allow plugin system for input.
A new plugin added should provide a format
and an input
, all fields containing that format should be that input.
Create other packages for specific internally developed inputs
In form page add ability to navigate to a linked relationship object in one-to-one and many-to one relationship on select field
Today there is no server side validation except for Prisma exception that can get thrown when saving the data (for example when saving an incorrect type or a mandatory column).
This is not enough and the library should provide a way to add validation for a specific fields, for example if we want to validate an email.
import { z } from "zod";
const options: NextAdminOptions = {
model: {
user: {
list: {
fields: {
email: {
search: true,
display: true,
validate: email => z.string().email().parse(email),
}
}
}
}
}
};
Errors should be displayed under each invalid fields.
Currently we use a constant to determine base path, this should be either automatic if possible or an option to pass to the library.
Showing related fields in a Select input
For example we have two tables Country and City, the City table has foreign key countryId,
In toString property we could specify a related field like: (city) => ${city.name} - ${city.country.name}
,
The same in the formatter property.
options.tsx
....
model: {
City: {
title: "Cities",
toString: (city) => `${city.name} (${city.country.name})`,
list: {
display: ["name", "country"],
fields: {
country: {
formatter: (country) => {
return <strong>{country?.name}</strong>;
},
}
}
},
edit: {
display: ["name", "latitude", "longitude", "country"]
}
},
}
It seems the package is published without dist 😨
Add the possibility of adding a function to fields in the options for formatting columns:
model: {
user: {
list: {
fields: {
id: {
formatColumn: (id) => id
}
}
}
}
}
The function must be typed, using the field type
We display the name of the model "as it" in the left-hand menu and in the section titles. The user should be able to override this title with a string (like "Utilisateurs") in the model config:
We can use a title
field
Sort relationship columns based on count
A table can't show Decimal fields (latitude, longitude) - MySQL
I'm getting warning in console:
Warning: Only plain objects can be passed to Client Components from Server Components. Decimal objects are not supported.
{id: ..., name: ..., country: ..., latitude: Decimal}
Model:
model City {
id Int @id @default(autoincrement())
name String
latitude Decimal @db.Decimal(10, 8)
longitude Decimal @db.Decimal(10, 8)
countryId Int
country Country @relation(fields: [countryId], references: [id])
}
Linked to ticket #14
Provide for relationships to be able to format the linked object, not just the id.
In One-to-Many relationships, the side carrying the relationship doesn't need to display the link (usually the id) but the object, so that it can be formatted.
The type of the formatColumn function must be overloaded to tolerate objects of type Collection
.
Delete the fields carrying the relationship to display only the relationship to the object
In the example :
model Post {
author User @relation("author", fields: [authorId], references: [id])
authorId Int
}
Remove from the json-schema
the authorId field for the formatted version of the author object, if there is no formatting function return only the id
Explore the possibility of Prisma extensions: https://www.prisma.io/docs/concepts/components/prisma-client/client-extensions/model#example. This would avoid having to modify the schema manually
A user should be able to use a file input, handle upload and return a blob or file to save in the form.
I'm using MySQL. After update (3.2.0) update/create doesn't work when on form present Select input for related field
It's works only if I remove "class" field from edit.display array
When I'm editing a record and didn't choose any value in Select input ("class" field), I'm getting error:
Invalid
prisma.timetable.update()invocation: { where: { id: "clradekj60001uyo099jqa6vw" }, data: { start: "2024-01-08T07:00:00.000Z", end: "2024-01-08T08:30:00.000Z", class: { disconnect: true, ~~~~~~~~~~ ? create?: ClassCreateWithoutTimetableInput | ClassUncheckedCreateWithoutTimetableInput, ? connectOrCreate?: ClassCreateOrConnectWithoutTimetableInput, ? upsert?: ClassUpsertWithoutTimetableInput, ? connect?: ClassWhereUniqueInput, ? update?: ClassUpdateToOneWithWhereWithoutTimetableInput | ClassUpdateWithoutTimetableInput | ClassUncheckedUpdateWithoutTimetableInput } } } Unknown argument
disconnect. Did you mean
connect? Available options are marked with ?.
If I chose some value in Select input and try to save record, I'm getting another error:
Unhandled Runtime Error
Error: Unexpected token 'c', "cvb9xc7v8b" is not valid JSON
⨯ node_modules\@premieroctet\next-admin\dist\utils\server.js (292:56) @ parse
⨯ SyntaxError: Unexpected token 'c', "cvb9xc7v8b" is not valid JSON
options,tsx:
model: {
Timetable: {
list: {
display: ["start", "end", "class"],
fields: {
"class": {
formatter: (classObj) => {
return <strong>{classObj.name}</strong>;
},
}
}
},
edit: {
display: ["id", "start", "end", "class"]
}
},
},
prisma
model Timetable {
id String @id @default(cuid())
start DateTime
end DateTime
note String?
classId String
class Class @relation(fields: [classId], references: [id], onDelete: Cascade)
@@index([classId], name: "idx_classId")
}
model Class {
id String @id @default(cuid())
name String
schoolAddreesId String
schoolAddress SchoolAddress @relation(fields: [schoolAddreesId], references: [id])
Timetable Timetable[]
User User[]
@@index([schoolAddreesId], name: "idx_class_schoolAddressId")
}
For the moment, the table identification field is fixed at id
Add the option of naming the identification field (@id
in the Prisma schema) by another name
A user should be able to use a JSON field
from Prisma : https://www.prisma.io/docs/concepts/components/prisma-client/working-with-fields/working-with-json-fields
Looks like there is no ability to customize names for columns in a table and labels of fields in a form.
Include the possibility of overriding inputs, for example if you just want to display the date instead of a disabled input, or an image instead of a text field... etc.
const options : NextAdminOptions = {
modelOptions : {
user : {
toString : (user : User) => `${user.name} (${user.email})`,
list : {
fields : {
id : {
search : true,
},
name : {
search : true,
},
email : {
search : true,
},
role : {},
createdAt : {
formatter : (createdAt) => user.createdAt.toLocaleDateString(),
}
}
},
edit : {
fields : {
createdAt : {
input : (createdAt) => {
return <input type="date" value={ createdAt.toString() } />
}
}
}
}
},
}
} ;
Does next-admin supports enums?
enum Sex {
man
woman
}
I'm using MySQL.
When I'm trying to delete record, I'm getting message in console of next.js app:
deleteAction not provided. Delete buttons will have no effect
Setting up compatibility for the App Router
First of all, great work on making this next admin, works really good.
I was wondering if it's possible to handle more than one prisma client? Right now we have multiple prisma clients that connect to different dbs, and would be really nice to be able to spin up different next admins with these connections within the same next project (so /prisma-1 would use 1 connection + types and /prisma-2 would use another one and so on).
Is this something feasible?
Thanks in advance
Currently the release process is a bit messy, since merging a PR on main
triggers a deploy on the production documentation and example.
The idea would be the following:
main
, we deploy the example and documentation in a beta
environmentmain
, we deploy the example and documentation in the production environmentGiven the following edit configuration:
edit: {
fields: {
id: {
display: false,
},
email: {
display: false,
},
name: {
display: false,
},
},
},
Accessing the ressource edit form makes the app crash with a Prisma error because the select statement is empty
Accessing the ressource edit form should not crash and we should see at least one field, for example the id
Based on the template field system, add the ability to hide templates:
Note that if the user wants to display a template without making a configuration, he can enter the template as follows:
session: {}
React Json Schema for input for date-time
use datetime-locale
type input, so data stored from this input is locale
Is there a way to translate the terms used in the GUI?
Looks like there is not way to show an user that uploading of file was failed. I'm talking about this example:
avatar: {
format: "file",
handler: {
/*
* Include your own upload handler here,
* for example you can upload the file to an S3 bucket.
* Make sure to return a string.
*/
upload: async (file: Buffer) => {
return "https://www.gravatar.com/avatar/00000000000000000000000000000000";
},
},
A form for creating and updating does not keeping order of fields which were specified in edit.display property
edit: {
display: [
"id",
"field1",
"field2",
],
For the moment we are using the model name "as it" in the url :
model User {
id Int @id @default(autoincrement())
}
Returns: https://next-admin-po.vercel.app/admin/User
You'd have to use a slugified version in lowercase :
In the case of a Date
property, give the field option to choose between date
, date-time
or time
in the field options.
I'm using MySQL and I'm getting error when I try to search.
Invalid
prisma.user.findMany()invocation: { select: { id: true, name: true, role: true, dob: true, city: true, slug: true }, where: { OR: [ { name: { contains: "pizza", mode: "insensitive" } }, { email: { contains: "pizza", mode: "insensitive" } } ] }, orderBy: {}, skip: 0, take: 10 } Unknown argument
mode. Did you mean
lte? Available options are marked with ?.
Looks like the problem in: mode: "insensitive"
According prisma doc
MySQL uses case-insensitive collation by default. Therefore, filtering with Prisma Client and MySQL is case-insensitive by default.
mode: 'insensitive' property is not required and therefore not available in the generated Prisma Client API.
But I'm not sure how.
Given the following list fields config
list: {
fields: {
id: {
search: true,
display: true,
},
name: {
search: true,
display: true,
},
team: {
search: true,
display: true,
},
accounts: {
display: true,
},
},
},
The list page is correctly working, however it is giving a Prisma error because it tries to count team
, which is a One-to-Many relation, therefore not possible to apply a count on it.
The One-to-Many relation should simply be ignored, eventually a warning should pop in dev environment. But it should be removed from the select statement for the count.
For some reason id column in a table always visible, even if "id" not specified in array in list.display property.
Given the following model config
User: {
toString: (user) => user.name!,
list: {
fields: {
name: {
search: true,
display: true,
},
team: {
search: true,
display: true,
},
accounts: {
display: true,
},
},
},
},
Clicking on a row in the users list should navigate to the user edition form
Navigation leads to an error as is navigates to an undefined
id. The URL we navigate to is http://localhost:3000/admin/User/undefined
.
Adding id
in the fields list fixes the issue
Possibility of using some html editor library (Rich text editor) in fields configuration
Currently we don't have any feedback on page navigation.
Can't create a record if optional Select field (related) on a form is empty.
I'm getting error:
Invalid
prisma.school.create()invocation: { data: { name: "test name", info: "test info", slug: "test-slug", ownerUser: false ~~~~~ } } Argument
ownerUser: Invalid value provided. Expected UserCreateNestedOneWithoutSchoolInput, provided Boolean.
At the same time I can update a record when Select field is empty.
Why Search input shows when the property [model-name].list.search does not exists in options file?
There is pretty common pattern, when we have to select values sequentially in comboboxes, for example:
Country > State > City
or
Category > Subcategory
then save into the table last chosen item (id of a city or subcategory)
So what about to make it possible via options.ts, something like this in display section:
model: {
user: {
edit: {
display: [
"name",
"email",
["Country", "State", "City"]
],
fields: {
Country: {
formatter: (Country) => {
return <strong>{Country.name}</strong>;
},
},
State: {
formatter: (State) => {
return <strong>{State.name}</strong>;
},
},
City: {
formatter: (City) => {
return <strong>{City.name}</strong>;
},
},
}
}
In form, add formatted value of a field to searched fields
Hi, first of all thank you for all the work you've put into this.
I'm having trouble with the search bar since some of the fields I have are UUIDs in a Postgres database and the contains
keyword is incompatible with that native type. Is there a way to be able to search through these fields so far? I'm using the latest version 3.2.6.
Invalid `prisma.user.findMany()` invocation:
{
select: undefined,
where: {
OR: [
{
id: {
contains: "A",
mode: "insensitive"
}
},
{
externalUserId: {
contains: "A",
mode: "insensitive"
}
},
{
personId: {
contains: "A",
mode: "insensitive"
}
}
]
},
orderBy: {},
skip: 0,
take: 10
}
Unknown argument `contains`. Available options are marked with ?.
This is what my Prisma schema looks like:
model User {
id String @id @default(uuid()) @db.Uuid
status UserStatus @default(CREATED)
externalUserId String @unique @map("external_user_id")
personId String @map("person_id") @db.Uuid
createdAt DateTime @default(now()) @map("created_at") @db.Timestamptz
updatedAt DateTime @updatedAt @map("updated_at") @db.Timestamptz
person Person @relation(fields: [personId], references: [id])
@@map("user")
}
And this is the table definition:
CREATE TABLE "user" (
"id" UUID NOT NULL,
"status" "user_status_enum" NOT NULL DEFAULT 'active',
"external_user_id" TEXT NOT NULL,
"person_id" UUID NOT NULL,
"created_at" TIMESTAMPTZ NOT NULL DEFAULT CURRENT_TIMESTAMP,
"updated_at" TIMESTAMPTZ NOT NULL,
CONSTRAINT "user_pkey" PRIMARY KEY ("id")
);
Also, if I specify which fields to search through (i.e. name
) I get console logs of SQL queries containing a WHERE 1=0
clause with no results. Any idea what could be the source of this problem?
model: {
User: {
list: {
display: ["id", "status", "externalUserId", "personId", "createdAt", "updatedAt"],
search: ["status"],
},
edit: {
display: ['id', 'status', 'personId', 'createdAt'],
},
},
}
> prisma:query SELECT "public"."user"."id", "public"."user"."status"::text, "public"."user"."external_user_id", "public"."user"."person_id", "public"."user"."created_at", "public"."user"."updated_at" FROM "public"."user" WHERE 1=0 ORDER BY "public"."user"."id" ASC LIMIT $1 OFFSET $2
> prisma:query SELECT COUNT(*) FROM (SELECT "public"."user"."id" FROM "public"."user" WHERE 1=0 OFFSET $1) AS "sub"
Thank you very much.
A declarative, efficient, and flexible JavaScript library for building user interfaces.
🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.
TypeScript is a superset of JavaScript that compiles to clean JavaScript output.
An Open Source Machine Learning Framework for Everyone
The Web framework for perfectionists with deadlines.
A PHP framework for web artisans
Bring data to life with SVG, Canvas and HTML. 📊📈🎉
JavaScript (JS) is a lightweight interpreted programming language with first-class functions.
Some thing interesting about web. New door for the world.
A server is a program made to process requests and deliver data to clients.
Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.
Some thing interesting about visualization, use data art
Some thing interesting about game, make everyone happy.
We are working to build community through open source technology. NB: members must have two-factor auth.
Open source projects and samples from Microsoft.
Google ❤️ Open Source for everyone.
Alibaba Open Source for everyone
Data-Driven Documents codes.
China tencent open source team.