Introduction
We try to build Prisma db CRUD tables with ability to customize this tables with beautiful UI.
NOTE: We have already Full stack projects With NextJS and GatsbyJS
Install
yarn add @paljs/admin
or
npm i @paljs/admin
CONTENT
Settings
To be able to custom your tables you need to generate adminSettings.json
file and use it as DB to get table settings from it.
Generate settings schema
1- with our cli pal g
add in your pal.js
config file this settings
frontend: {
admin: true,
},
2- with code use our UIGenerator
class
Add graphql queries and mutation
To be able to update adminSettings.json
file with your custom setting in a beautiful UI you have to add 1 query to pull schema in frontend and 2 mutations one for an update model settings and other for update field.
If you use Nexus
all you need to be sure you added nexus-paljs plugin in your backend
If you use another way you need to add these resolvers and typeDev to your backend graphql schema
yarn add lowdb
1- create resolver file and add
import low from 'lowdb';
import FileSync from 'lowdb/adapters/FileSync';
const adapter = new FileSync<{
[key: string]: { [key: string]: { [key: string]: any }[] }[];
}>('prisma/adminSettings.json');
const db = low(adapter);
export default {
Query: {
getSchema: () => {
return db.value();
},
},
Mutation: {
updateModel: (_parent, { id, data }) => {
return db.get('models').find({ id }).assign(data).write();
},
updateField: (_parent, { id, modelId, data }) => {
return db.get('models').find({ id: modelId }).get('fields').find({ id }).assign(data).write();
},
},
};
2- create typeDefs file and add
import gql from 'graphql-tag';
export default gql`
type Schema {
enums: [Enum!]!
models: [Model!]!
}
type Query {
getSchema: Schema!
}
type Mutation {
updateField(data: UpdateFieldInput, id: String!, modelId: String!): Field!
updateModel(data: UpdateModelInput, id: String!): Model!
}
type Enum {
fields: [String!]!
name: String!
}
type Model {
create: Boolean!
delete: Boolean!
displayFields: [String!]!
fields: [Field!]!
id: String!
idField: String!
name: String!
update: Boolean!
}
type Field {
create: Boolean!
editor: Boolean!
filter: Boolean!
id: String!
isId: Boolean!
kind: KindEnum!
list: Boolean!
name: String!
order: Int!
read: Boolean!
relationField: Boolean
required: Boolean!
sort: Boolean!
title: String!
type: String!
unique: Boolean!
update: Boolean!
}
input UpdateFieldInput {
create: Boolean
editor: Boolean
filter: Boolean
id: String
isId: Boolean
kind: KindEnum
list: Boolean
name: String
order: Int
read: Boolean
relationField: Boolean
required: Boolean
sort: Boolean
title: String
type: String
unique: Boolean
update: Boolean
}
input UpdateModelInput {
create: Boolean
delete: Boolean
displayFields: [String!]
fields: [UpdateFieldInput!]
idField: String
name: String
update: Boolean
}
enum KindEnum {
enum
object
scalar
}
`;
Add Settings React Component
You have Settings
react component to change your tables settings.
NOTE: You must add this component under ApolloProvider
component because we use @apollo/client
to query and update settings.
Now you can add our component to any page like this.
import React from 'react';
import { Settings } from '@paljs/admin/Settings';import '@paljs/admin/style.css';
export default function SettingsPage() {
// Settings component not have any props return <Settings />;
}
When you open this in your browser will get it like this image.
Models card
- Models select menu: to change between your schema models.
- Database Name: your original Model name like your schema.prisma.
- Display Name: Model name to display in model table page.
- Id field: field that have
@id
attribute in your model. - Display Fields you can select one or more to display in relation tables.
- Actions add actions to your table create, update and delete.
Fields Accordions
order you can sort this fields in table view, update form and create form by Drag and Drop.
Every field has Accordion with this content:
- Database Name: your original Field name like your schema.prisma.
- Display Name: it will display into table page, update form and create form.
- Actions
- read show this field in table view.
- create show this field in create record form.
- update show this field in update record form.
- filter add filter option to this field in table if read checked.
- sort add sortBy option to this field in table if read checked.
- editor use the
Editor
component in the update and create. You must send this component intoformInputs
prop because we do not have any default one for this option. - upload use the
Upload
component in the update and create. You must send this component intoformInputs
prop because we do not have any default one for this option.
Props
language
object with the language keys
const defaultLanguage = {
dir: 'ltr',
header: 'Update models Tables',
dbName: 'Database Name',
displayName: 'Display Name',
modelName: 'Model Name',
idField: 'Id Field',
displayFields: 'Display Fields',
fieldName: 'Field Name',
actions: 'Actions',
create: 'create',
update: 'update',
delete: 'delete',
read: 'read',
filter: 'filter',
sort: 'sort',
editor: 'editor',
upload: 'upload',
tableView: 'Table View',
inputType: 'Input Type',
};
Prisma table
React component to list, create, update and delete your model data.
NOTE: This component use 3 queries (findUnique, findMany, findManyCount) and 3 mutations (createOne, updateOne, deleteOne) be sure to add them in your backend by using our CLI pal generate
Using with NextJS
Adding style to _app.tsx
file.
src/pages/_app.tsx
import '@paljs/admin/style.css';
src/Components/PrismaTable.tsx
import React from 'react';
import { useRouter } from 'next/router';
import { PrismaTable } from '@paljs/admin/PrismaTable';
const Table: React.FC<{ model: string }> = ({ model }) => {
const router = useRouter();
return <PrismaTable model={model} push={router.push} query={router.query} />;};
export default Table;
Using with GatsbyJS
src/components/PrismaTable.tsx
import React from 'react';
import { PrismaTable } from '@paljs/admin/PrismaTable';import { navigate } from 'gatsby';
import { useLocation } from '@reach/router';
import * as queryString from 'query-string';
import '@paljs/admin/style.css';
const Table: React.FC<{ model: string }> = ({ model }) => {
const location = useLocation();
const query = queryString.parse(location.search);
return <PrismaTable model={model} push={navigate} query={query} />;
};
export default Table;
Props
Prisma Table has many props to can custom it like you want.
To customize tableColumns
and formInputs
components you need to look to default components and have good react skills.
interface ModelTableProps {
// customize your table permissions and overwrite the settings it's allow you to give users different permissions
actions?: ('create' | 'update' | 'delete')[]; // model name like in `schema.prisma` file
model: string; // push function to move between tables and change link
push: (url: string) => void; // link query object used in filters `?id=1` => {id: 1}
query: { [key: string]: any }; // model pages path you must have all models pages in same path to can move between them.
// default `/admin/models/`
pagesPath?: string; // in table pagination you can select pageSize you can pass this options here.
// default: [10, 20, 30, 40, 50, 100]
pageSizeOptions?: number[]; // moving between table pages. we not show you all available pages we just see current page and other 3 options.
// default: 4
paginationOptions?: number; // add a checkbox for every record on the table you can use for custom cases like delete many
onSelect?: (values: any[]) => void; // this event call when you click cancel button in create record modal
onCancelCreate?: (options: { model: string; setCreateModal: (state: boolean) => void }) => void; // this event call when you click save button in create record modal
onSaveCreate?: (options: { model: string; setCreateModal: (state: boolean) => void; refetchTable: (options?: any) => void; }) => void; // this event call when you click cancel button in edit record page
onCancelUpdate?: (options: { model: string }) => void; // this event call when you click save button in edit record page
onSaveUpdate?: (options: { model: string; refetchTable: (options?: any) => void }) => void; // In create and update form when you click save this function will call before take every value to apollo mutation
// Here we handle numbers list json values you can use it if you need to add any featuer
valueHandler?: (value: string, field?: SchemaField, create?: boolean) => any; // it's function return object with react table columns https://github.com/tannerlinsley/react-table
// default here: https://github.com/paljs/prisma-tools/blob/master/admin/src/PrismaTable/Table/Columns.tsx
tableColumns?: GetColumnsPartial; // it's object with form input components for every field type we use this package https://react-hook-form.com/
// default here: https://github.com/paljs/prisma-tools/blob/master/admin/src/PrismaTable/Form/Inputs.tsx
formInputs?: Partial<FormInputs>; // you can customize the actions buttons
actionButtons?: {
Add?: React.FC;
Update?: React.FC<{ id: any }>;
Delete?: React.FC<{ id: any }>;
};
lang: typeof Language;
dir: 'rtl' | 'ltr';
}
type FormInputs = Record<'Default' | 'Editor' | 'Enum' | 'Object' | 'Date' | 'Boolean', React.FC<InputProps>>;
interface InputProps {
field: SchemaField;
value: any;
data: any;
error: any;
// import { UseFormReturn } from 'react-hook-form';
register: UseFormReturn['register'];
setValue: UseFormReturn['setValue'];
getValues: UseFormReturn['getValues'];
watch: UseFormReturn['watch'];
disabled: boolean;
}
// import {Column,UseFiltersColumnOptions,UseSortByColumnOptions} from 'react-table';
type Columns = Record<
'boolean' | 'number' | 'enum' | 'DateTime' | 'object' | 'string' | 'list',
Column & UseFiltersColumnOptions<any> & UseSortByColumnOptions<any>
>;
export type GetColumnsPartial = (field: SchemaField, model?: SchemaModel) => Partial<Columns>;
The default language object
const Language = {
yes: 'yes',
no: 'no',
all: 'All',
startDate: 'Start Date',
endDate: 'End Date',
min: 'Min',
max: 'Max',
range: 'Range',
equals: 'Equals',
deleteConfirm: 'Are you sure you want to delete this record ?',
select: 'Select',
actions: 'Actions',
relation: 'Relation',
viewAll: 'View All',
viewRelated: 'View Related',
connected: 'Connected',
connect: 'Connect',
disConnect: 'DisConnect',
editRow: 'Edit Row',
viewRow: 'View Row',
deleteRow: 'Delete Row',
showing: 'Showing',
of: 'of',
results: 'results',
goToFirstPage: 'Go to first page',
goToLastPage: 'Go to last page',
goPageNumber: 'Go Page Number',
setPageSize: 'Set page size ',
filter: 'Filter',
save: 'Save',
cancel: 'Cancel',
close: 'close',
create: 'create',
update: 'update',
view: 'view',
isRequired: ' is required',
show: 'SHOW',
clear: 'clear',
clearAll: 'Clear All',
};
Generate pages
Now we need to generate a page for every model with our prisma table here src/components/PrismaTable.tsx
.
You can add them manulay or use our cli pal generate
Add to your pal.js
config file
frontend: {
admin: boolean or object,
},
- Add true To generate pages with default settings
- you can customize it by add object with this proparty AdminPagesOptions
How to add and update list values?
As we know we can use list values with prisma but how we work on this fields in admin forms
Example
model SchemaModel {
id Int @id @default(autoincrement())
string String[]
integer Int[]
boolean Boolean[]
float Float[]
json Json[]
enums FieldKind[]
}
enum FieldKind {
object
enum
scalar
}
This fields inputs will ba as text type
Int[]
=>1,2,3,4
converted to[1,2,3,4]
Flout[]
=>1.2,2.3,3.5
converted to[1.2,2.3,3.5 ]
String[]
=>first,second
converted to['first','second']
Boolean[]
=>true,false
converted to[true,false]
enum[]
=>object,enum
converted to['object','enum']
Json[]
=>[{"first": 1},{"second": 2}]
we will pass value throughtJSON.parse()