Based on Vite (GitHub). Based on Pinia (GitHub). Based on Vuetify (GitHub). Based on Vue Easytable (GitHub). Based on Vee Validate (GitHub).
And what you see in package.json.
Best to be used coupled with Volcanic Backend (GitHub).
This project cannot be imported as a dependency, therefore you must clone it and modify it as you need. The project is structured so that most of the change should be made inside the configuration folder, so you can clone the project again when you want to update the codebase and just paste inside your confguration folder.
VITE_API_BASE_URL = http://0.0.0.0:2230
yarn dev
yarn build //to build for production, with type check
yarn build-no-type-check //to build for production, without type check
We wanted to develope a super easy and super fast to configure admin panel. To start you have to configure 2 files:
- configuration/menu.ts
- configuration/rest.default.ts
and you are good to go. There is also a user page to change the user password.
But you would miss all the fun!
The Admin is thought to be used as a desktop web app, but there is a simplified table support if the web app is loaded on a phone device.
The project is localized in 3 languages: English, Italian and German. The configuration files can be optionally localized as described for every specific section.
type Menu = Array<MenuItem>
interface MenuItem {
label:
| string
| {
[language: string]: string
}
name: string | null
source: string | null
icon?: string
isDefault?: boolean
filters?: TableFiltersParams
sorting?: TableSortingParams
pagination?: TablePaginationParams
operation?: () => void
}
The fields label, name and source are the bare minimum for the menu.
- label is the string that the user will see in the menu. The label can be localized,
ex:
{ en: 'My Label', it: 'La mia etichetta', de: 'Mein Etikett' }
- name is the identifier for the scope, as we will see, you can have more menu items for a single data source
- source describes the name of the data source. If your api is GET: /cocktails -> to get all the cocktails on the DB, then the source you have to set is "cocktails".
icon is the name of the Material Design you want to pair with the menu item, the name must be written so that is compatible with Vuetify specifications. isDefault sets the page linked by that menu item as the default landing page. filters lets you pre-filter the data source, the structure is:
Array<{
key: string
operator: string
value: ApiGenericValue
}>
where key is the field you want to filter for, operator is the filter operator (it depends on your apis), value is the value you want to filter for (boolean, string or number). sorting lets you pre-sort the data source, the structure is
{
[key:string]: string
}
pagination lets you pre-paginate the data source, the structure is
{
pageIndex?: number
pageSize?: number
}
operation is a function you can trigger on menu item press, it is executed in case the source field has no value.
{
remapResponse: function (source: string, data: ApiResponseBody) {}
remapSource: function ({ filters, sorting, pagination }: TableRoutingParams) {}
}
remapResponse is used to change the response in case they are not standard CRUD responses. Should always return
return data
where the data variable is the data you want to send back to the table or detail page. The expected data are:
- from a count the table expects a number
- from a find the table expects an array of objects
- from a findOne the detail page expects an object
- from a update the table expects an object
- from a delete the table expects an object
remapSource is used to translate the parameters coming from the volcanic admin table so that could be executed by your backend server. Should always return
return encodeURI(url)
where the url variable is the url you want to call your backend with.
interface ConfigurationCompany {
name?: string
logo?: string
logo_alt?: string
logo_width?: string | number
logo_height?: string | number
}
You can use this file to configure the branding of your admin panel. name it should be the name of your company or project. It is used as a title tag for the logo image. logo is the name of the image file to be linked by a "img" tag for the light theme. logo_alt is the name of the image file to be linked by a "img" tag for the dark theme. logo_width is the width to be used for the logo, the default value is "auto". logo_height is the height to be used for the logo, the default value is "50px", the height of the vuetify header is "48px".
interface Authconfiguration {
request: {
body: { reqEmailField: string; reqPasswordField: string }
url: string
method: 'POST' //for now only POST is supported
}
response: {
body: {
resEmailField: string
resAuthTokenField: string
resRolesField: string
}
}
}
This is the object you can configure to manage the authentication. Authentication is set by default, with default fields. An example configuration is:
const authenticationConfig = {
request: {
body: { reqEmailField: 'username', reqPasswordField: 'password' },
url: '/auth/login',
method: 'POST'
},
response: {
body: {
resEmailField: 'email',
resAuthTokenField: 'token',
resRolesField: 'roles'
}
}
}
export default authenticationConfig
interface ConfigSourceModel {
columns: {
[key: string]: {
type: 'number' | 'date' | 'text' | 'boolean'
input: {
type: 'autocomplete' | 'select' | 'input' | 'calendar' | 'textarea'
condition?: {
field: string
operator: 'eq' | 'neq' | 'lt' | 'lte' | 'mt' | 'mte'
value: string | number | boolean
}
label?:
| string
| {
[language: string]: string
}
defaultValue?:
| string
| boolean
| number
| {
[language: string]: string
}
readonly?: boolean
disabled?: boolean
scope?: 'create' | 'update' | 'all'
hidden?: boolean
source?: string | 'static'
filters?: { [key: string]: string }
sorting?: { [key: string]: 'ASC' | 'DESC' }
isKey?: boolean
specifications?: {
subtype: 'currency' | 'datetime' | 'multiple'
symbol?: string
}
data?:
| number
| {
[field: string]: any
}
| Array<{
[field: string]: any
}>
dataOptions?: {
value: string
label: string | Array<string>
}
options?: {
style?: string
format?: string
validation?: {
methods?: Array<
| 'alpha'
| 'alpha_dash'
| 'alpha_num'
| 'alpha_spaces'
| 'between'
| 'confirmed'
| 'digits'
| 'dimensions'
| 'email'
| 'excluded'
| 'ext'
| 'image'
| 'oneOf'
| 'integer'
| 'is'
| 'is_not'
| 'length'
| 'max'
| 'max_value'
| 'mimes'
| 'min'
| 'min_value'
| 'numeric'
| 'regex'
| 'required'
| 'required_if'
| 'size'
| 'double'
>
value?: string | number
}
layout?: {
tab?: {
title?: string
}
}
}
table?: {
cell?: {
format?: Function | string
isLink?: boolean
}
visible?: boolean
width?: string | number
sorting?: {
enabled: boolean
subField?: string
default?: 'asc' | 'desc'
}
filtering?: {
enabled: boolean
operator:
| 'between' //case with 2 values
| 'in' //array case, with 2+ value
| 'nin' //array case, with 2+ value
| 'eq'
| 'neq'
| 'eqi'
| 'neqi'
| 'gt'
| 'ge'
| 'lt'
| 'le'
| 'starts'
| 'ends'
| 'startsi'
| 'endsi'
| 'like'
| 'contains'
| 'ncontains'
| 'likei'
| 'containsi'
| 'ncontainsi'
| 'null'
| 'notNull'
defaultValues?: Array<string | number | boolean>
subField?: string
}
}
}
}
}
layout?: {
tabs?: {
[name: string]: {
title:
| string
| number
| boolean
| {
[language: string]: string | number | boolean
}
}
}
}
table?: {
pagination?: {
pageSize: number
}
options?: {
canDelete?: boolean
checkbox?: boolean
}
rowMenu?: [
{
title:
| string
| number
| boolean
| {
[language: string]: string | number | boolean
}
requiresConfirmation: boolean
delete?: boolean
operation?: (value: any) => void
}
]
customColumns?: Array<{
title?:
| string
| number
| boolean
| {
[language: string]: string | number | boolean
}
align?: 'center' | 'left' | 'right'
position: number
customComponent: JSXComponent
}>
}
}
This is the source configuration. The input field has 3 optionally localized fields: label, defaultValue and the title of the layout configuration. The syntax is, in case you want to change the value fo these fields based on locale language, is:
{
label: {
en: 'Name',
it: 'Nome',
de: 'Vorname'
},
defaultValue: {
en: '+1',
it: '+39',
de: '+49'
}
}
There are 2 levels of configuration:
- fully automatic in which you just set the "menu.ts" and "rest.default.ts" files, in this case the table shows every field and the detail page too, considering every field as required of a string type.
- configured in which you have to set a sources/.ts file.
columns is an Object, every Key should be equal to the source which is the name of the file and value of the source field of the menu.ts . For every column you set the type and input. The table configuration could be set to configure globally the table, for pagination and to configure the menu button on every row. The menu button can be configured with a title, a requiresConfirmation to request the user a confirmation before executing the operation and an operation custom function, if the delete is set to "true", the operation function is ignored and the standard "delete" endpoint is called passing the row id.
The input field is the most complex and it lets you set both the behavior of the single input inside the detail page and the column and cell inside the table. Many sub-fields are self explicable, we are going to explain the most peculiar ones:
- isKey configures the field as the database key, useful to be used with updates, findOnes and deletes. The default key is the field with value "id".
- specifications configures some specific implementations of the input components, the sub fields are:
- subtype which allows you to set the field to be treated as a "currency" (in this case the subfield "symbos" is considered), "datetime" to allow the "calendar" input to show both days and time and "multiple" for "autocomplete" and "select" inputs to let the user choose multiple values.
- symbol is the symbol used in case of the subtype is set to "currency".
- condition sets a condition for the visibility of the input inside the detail page, the value of the field is checked through the operator and value, so that if row[field] value === true then the input will be shown. If there is now condition set, the input will be always shown.
- scope defines if the input should be shown in update, create or always. The default value is "all".
- options has sub options:
- style to set a custom styling
- format a string to format a value (for now only for dates)
- validation is set to configure the validation, the default is "required", the sub fields are:
- methods is an array of vee-validate rules
- value is a value to be compared to
Fields to configure the options of "select" and "autocomplete" inputs are:
- source is the endpoint to call to populate the list of the options, should be set to "static" to apply the data field as a static list of options
- data is the static list of options to apply
- dataOptions configures the "value" and "label" keys to match the single item of the values returned calling the "source" endpoint
- filters and sorting are the filters and sorting configuration applied when calling the "source" enpoint
The table field inside the input configuration, configures the behavior of the column and single cell inside the table.
- cell sets the cell configuration with 2 sub fields:
- format could be a function or a string to format the data before showing it to the user, the string is use to format "boolean" and "date" fields.
- isLink sets the cell as a link, useful in case of relation fields
- sorting and filtering are set to configure the default parameters to apply to the column, you can apply only 1 filter per column.
- subField is the field to check inside a relation field, ex: &sort:cocktail.ingredient=asc .
- enable you can use this field to disable sorting or filtering for that column, they are true by default.
- defaultValues are the default values to be set for the filter, it is an array to be used in caso of "between", "in" or "nin" operator values.
- operator are a self explicable list of operators to filter the column with.
The table base field lets you to configure the table in the whole.
- pagination: has the pageSize field and it configures the pageSize of the table.
- options: has the canDelete field to show the options to delete the rows and checkbox field, to enable the multi selection.
- rowMenu: is an array of object, the object has the title which is a localized label, the requiresConfirmation which configures the need to ask for a user confirmation before starting the operation or delete, the delete option, which tells that you need to perform a delete, in this case the operation field is not executed, the operation field which is a custom function that will be executed.
- customColumns: is an array of object, the object has the title which is a localized label, the align field, which sets the alignment inside the cell, position which is the number of the column, the count of the column starts from 0, customComponent which is a custom JSX component.
You can configure 2 types of layout
- default: in this case you'll get a simple list of imputs in a single column, but you don't have to do anything special
- tabs: for this configuration you can organize the inputs in tabs, every input must be associated to a options.layout.name value that is matched with the layout.tabs keys. The tabs title can be translated.