Views
Views are the most important part of Minoro, they are used to display other types of content such as the ones below. They are displayed in the sidebar and can be used to navigate to different pages in the admin panel.
In a traditional CRUD style admin panel, you would have views such as "All Posts", "Create Post" and "Edit Post".
Views contain elements that are used to actually display content in them.
Defining Views
To define views in Minoro, you have to use the defineView function. This function allows you to create a view with a unique name and specify its elements.
Let's create a very simple view that displays a text element and a button under it.
// views/simpleView.ts
import { defineView } from 'minoro'
export const simpleView = defineView(
{ name: 'Simple view', icon: 'lucide:info' },
(view) => {
view.defineText({
text: 'This is a simple view with a button.',
})
view.defineButton({
label: 'Do something',
action: async () => {},
})
},
)A view needs a unique name, which is used to identify it in the admin panel. You can also specify an icon for the view, which will be displayed in the sidebar.
On its own, this view does nothing just yet, it needs to be added to the Minoro instance so that it can be displayed in the admin panel.
import { createMinoro } from 'minoro'
import { simpleView } from './views/simpleView'
const minoro = createMinoro({
views: [
simpleView, // <-- Add the view(s) here
],
allowedOrigins: ['*'],
ONE_TIME_ADMIN_TOKEN: 'super-secret-token',
async setup() {
return {
database: {
type: 'sqlite',
connection: `file:database.sqlite`,
},
}
},
})View params
Views can accept parameters, whicih can be used to populate the view's content dynamically. This is useful for creating views with forms that have default values.
Let's say we have a view that is used to edit a blog post.
Our view will need a parameter to identify which post we are editing, so we can fetch the post data from the database and prepopulate the form with it.
Let's see it in action:
import { defineView } from 'minoro'
import { z } from 'zod'
/** This is the schema representing the data needed to edit a post */
const PostEditSchema = z.object({
title: z.string(),
content: z.string(),
})
export const editPostView = defineView(
{
name: 'Post details',
params: z.object({ postId: z.string() }), // <-- Define params schema here, this is Standard Schema compatible
/**
* Hide this view from the list of views in the sidebar, because it needs params to be accessed.
* Check out the documentation for Datatables to see how to redirect to this view with the correct params.
*/
hidden: true,
},
(view) => {
view.defineForm({
name: 'Edit Post',
input: PostEditSchema, // <-- Use the schema to define the form input
jsonSchema: z.toJsonSchema(PostEditSchema), // <-- Convert the schema to JSON Schema for the form, this is what will generate the frontend fields
// Fetch the post in the database and prepopulate the form with its data
render: async () => {
const { postId } = view.getParams() ?? {}
// dummy sql query
const post = await sql`SELECT * FROM posts WHERE id = ${postId} LIMIT 1`.execute()
return {
defaultValues: post,
}
},
// When the form is submitted, update the post in the database
handler: async ({ fields, params }) => {
const { postId } = params
await db.update(postsTable).set(fields).where(eq(postsTable.id, postId))
},
})
},
)View options
type Options = {
/** Unique name of the view, used to identify it in the admin panel */
name: string
/** Standard Schema */
params?: unknown
/**
* Hide this view in the sidebar
* @default false
*/
hidden?: boolean
/**
* An icon to show next to the name in the sidebar, in the form of "iconpack:icon-name"
* @see https://icones.js.org/
*/
icon?: string
}