diff --git a/404.html b/404.html index 6bb03883d..d22690759 100644 --- a/404.html +++ b/404.html @@ -5,7 +5,7 @@ Page Not Found | AdminForth - + diff --git a/assets/js/3fdf0239.d522841b.js b/assets/js/3fdf0239.f8c7dd3e.js similarity index 96% rename from assets/js/3fdf0239.d522841b.js rename to assets/js/3fdf0239.f8c7dd3e.js index ed1d83c21..83f550ca6 100644 --- a/assets/js/3fdf0239.d522841b.js +++ b/assets/js/3fdf0239.f8c7dd3e.js @@ -1 +1 @@ -"use strict";(self.webpackChunkadminforth=self.webpackChunkadminforth||[]).push([[7132],{519:(e,n,i)=>{i.r(n),i.d(n,{assets:()=>c,contentTitle:()=>r,default:()=>u,frontMatter:()=>t,metadata:()=>l,toc:()=>a});var s=i(4848),o=i(8453);const t={},r=void 0,l={id:"Plugins/ForeignInlineList",title:"ForeignInlineList",description:"Foreign inline list plugin allows to display a list (table) of items from a foreign table in the show view.",source:"@site/docs/Plugins/ForeignInlineList.md",sourceDirName:"Plugins",slug:"/Plugins/ForeignInlineList",permalink:"/docs/Plugins/ForeignInlineList",draft:!1,unlisted:!1,tags:[],version:"current",frontMatter:{},sidebar:"tutorialSidebar",previous:{title:"AuditLog",permalink:"/docs/Plugins/AuditLog"},next:{title:"TypeDoc API",permalink:"/docs/api/"}},c={},a=[{value:"Usage",id:"usage",level:2}];function d(e){const n={a:"a",code:"code",h2:"h2",img:"img",p:"p",pre:"pre",...(0,o.R)(),...e.components};return(0,s.jsxs)(s.Fragment,{children:[(0,s.jsx)(n.p,{children:"Foreign inline list plugin allows to display a list (table) of items from a foreign table in the show view."}),"\n",(0,s.jsx)(n.h2,{id:"usage",children:"Usage"}),"\n",(0,s.jsx)(n.p,{children:"Import plugin:"}),"\n",(0,s.jsx)(n.pre,{children:(0,s.jsx)(n.code,{className:"language-ts",children:"import ForeignInlineListPlugin from 'adminforth/plugins/ForeignInlineListPlugin';\n"})}),"\n",(0,s.jsx)(n.p,{children:"If yu are using pure Node without TypeScript, you can use the following code:"}),"\n",(0,s.jsx)(n.pre,{children:(0,s.jsx)(n.code,{className:"language-js",children:"import ForeignInlineListPlugin from 'adminforth/dist/plugins/ForeignInlineListPlugin/index.js';\n"})}),"\n",(0,s.jsxs)(n.p,{children:["In ",(0,s.jsx)(n.a,{href:"/docs/gettingStarted",children:"Getting Started"})," we created a ",(0,s.jsx)(n.code,{children:"'apparts'"})," resource which has a field ",(0,s.jsx)(n.code,{children:"'user_id'"}),".\nThis field refers to record from ",(0,s.jsx)(n.code,{children:"'users'"})," resource. This means that we can display a list of appartments in the user's show view."]}),"\n",(0,s.jsxs)(n.p,{children:["Add to your ",(0,s.jsx)(n.code,{children:"'users'"})," resource configuration (which we created in ), plugin instance:"]}),"\n",(0,s.jsx)(n.pre,{children:(0,s.jsx)(n.code,{className:"language-ts",children:"{ \n ...\n resourceId: 'users',\n ...\n plugins: [\n new ForeignInlineListPlugin({\n foreignResourceId: 'apparts',\n modifyTableResourceConfig: (resourceConfig: AdminForthResource) => {\n // hide column 'square_meter' from both 'list' and 'filter'\n const column = resourceConfig.columns.find((c: AdminForthResourceColumn) => c.name === 'square_meter')!.showIn = [];\n resourceConfig.options!.listPageSize = 1;\n\n // feel free to console.log and edit resourceConfig as you need\n },\n }),\n ],\n}\n"})}),"\n",(0,s.jsxs)(n.p,{children:["You can use ",(0,s.jsx)(n.code,{children:"modifyTableResourceConfig"})," callback to modify what columns to show in the list and filter of the foreign table."]}),"\n",(0,s.jsx)(n.p,{children:(0,s.jsx)(n.img,{alt:"alt text",src:i(7771).A+"",width:"3458",height:"1823"})}),"\n",(0,s.jsxs)(n.p,{children:["See ",(0,s.jsx)(n.a,{href:"/docs/api/plugins/ForeignInlineListPlugin/types/type-aliases/PluginOptions",children:"API Reference"})," for more all options."]})]})}function u(e={}){const{wrapper:n}={...(0,o.R)(),...e.components};return n?(0,s.jsx)(n,{...e,children:(0,s.jsx)(d,{...e})}):d(e)}},7771:(e,n,i)=>{i.d(n,{A:()=>s});const s=i.p+"assets/images/localhost_3500_resource_users_show_maf3gn-cd12d0c625c41cb2120f2e0eb7e04a86.png"},8453:(e,n,i)=>{i.d(n,{R:()=>r,x:()=>l});var s=i(6540);const o={},t=s.createContext(o);function r(e){const n=s.useContext(t);return s.useMemo((function(){return"function"==typeof e?e(n):{...n,...e}}),[n,e])}function l(e){let n;return n=e.disableParentContext?"function"==typeof e.components?e.components(o):e.components||o:r(e.components),s.createElement(t.Provider,{value:n},e.children)}}}]); \ No newline at end of file +"use strict";(self.webpackChunkadminforth=self.webpackChunkadminforth||[]).push([[7132],{519:(e,n,i)=>{i.r(n),i.d(n,{assets:()=>c,contentTitle:()=>r,default:()=>u,frontMatter:()=>t,metadata:()=>l,toc:()=>a});var s=i(4848),o=i(8453);const t={},r=void 0,l={id:"Plugins/ForeignInlineList",title:"ForeignInlineList",description:"Foreign inline list plugin allows to display a list (table) of items from a foreign table in the show view.",source:"@site/docs/Plugins/ForeignInlineList.md",sourceDirName:"Plugins",slug:"/Plugins/ForeignInlineList",permalink:"/docs/Plugins/ForeignInlineList",draft:!1,unlisted:!1,tags:[],version:"current",frontMatter:{},sidebar:"tutorialSidebar",previous:{title:"AuditLog",permalink:"/docs/Plugins/AuditLog"},next:{title:"TypeDoc API",permalink:"/docs/api/"}},c={},a=[{value:"Usage",id:"usage",level:2}];function d(e){const n={a:"a",code:"code",h2:"h2",img:"img",p:"p",pre:"pre",...(0,o.R)(),...e.components};return(0,s.jsxs)(s.Fragment,{children:[(0,s.jsx)(n.p,{children:"Foreign inline list plugin allows to display a list (table) of items from a foreign table in the show view."}),"\n",(0,s.jsx)(n.h2,{id:"usage",children:"Usage"}),"\n",(0,s.jsx)(n.p,{children:"Import plugin:"}),"\n",(0,s.jsx)(n.pre,{children:(0,s.jsx)(n.code,{className:"language-ts",children:"import ForeignInlineListPlugin from 'adminforth/plugins/ForeignInlineListPlugin';\n"})}),"\n",(0,s.jsx)(n.p,{children:"If yu are using pure Node without TypeScript, you can use the following code:"}),"\n",(0,s.jsx)(n.pre,{children:(0,s.jsx)(n.code,{className:"language-js",children:"import ForeignInlineListPlugin from 'adminforth/dist/plugins/ForeignInlineListPlugin/index.js';\n"})}),"\n",(0,s.jsxs)(n.p,{children:["In ",(0,s.jsx)(n.a,{href:"/docs/gettingStarted",children:"Getting Started"})," we created a ",(0,s.jsx)(n.code,{children:"'apparts'"})," resource which has a field ",(0,s.jsx)(n.code,{children:"'user_id'"}),".\nThis field refers to record from ",(0,s.jsx)(n.code,{children:"'users'"})," resource. This means that we can display a list of appartments in the user's show view."]}),"\n",(0,s.jsxs)(n.p,{children:["Add to your ",(0,s.jsx)(n.code,{children:"'users'"})," resource configuration (which we created in ), plugin instance:"]}),"\n",(0,s.jsx)(n.pre,{children:(0,s.jsx)(n.code,{className:"language-ts",children:"{ \n ...\n resourceId: 'users',\n ...\n plugins: [\n new ForeignInlineListPlugin({\n foreignResourceId: 'apparts',\n modifyTableResourceConfig: (resourceConfig: AdminForthResource) => {\n // hide column 'square_meter' from both 'list' and 'filter'\n const column = resourceConfig.columns.find((c: AdminForthResourceColumn) => c.name === 'square_meter')!.showIn = [];\n resourceConfig.options!.listPageSize = 1;\n\n // feel free to console.log and edit resourceConfig as you need\n },\n }),\n ],\n}\n"})}),"\n",(0,s.jsxs)(n.p,{children:["You can use ",(0,s.jsx)(n.code,{children:"modifyTableResourceConfig"})," callback to modify what columns to show in the list and filter of the foreign table."]}),"\n",(0,s.jsx)(n.p,{children:(0,s.jsx)(n.img,{alt:"alt text",src:i(4064).A+"",width:"3458",height:"1823"})}),"\n",(0,s.jsxs)(n.p,{children:["See ",(0,s.jsx)(n.a,{href:"/docs/api/plugins/ForeignInlineListPlugin/types/type-aliases/PluginOptions",children:"API Reference"})," for more all options."]})]})}function u(e={}){const{wrapper:n}={...(0,o.R)(),...e.components};return n?(0,s.jsx)(n,{...e,children:(0,s.jsx)(d,{...e})}):d(e)}},4064:(e,n,i)=>{i.d(n,{A:()=>s});const s=i.p+"assets/images/localhost_3500_resource_users_show_maf3gn-cd12d0c625c41cb2120f2e0eb7e04a86.png"},8453:(e,n,i)=>{i.d(n,{R:()=>r,x:()=>l});var s=i(6540);const o={},t=s.createContext(o);function r(e){const n=s.useContext(t);return s.useMemo((function(){return"function"==typeof e?e(n):{...n,...e}}),[n,e])}function l(e){let n;return n=e.disableParentContext?"function"==typeof e.components?e.components(o):e.components||o:r(e.components),s.createElement(t.Provider,{value:n},e.children)}}}]); \ No newline at end of file diff --git a/assets/js/58574ab0.0202d015.js b/assets/js/58574ab0.66ef880b.js similarity index 98% rename from assets/js/58574ab0.0202d015.js rename to assets/js/58574ab0.66ef880b.js index 256bf4f88..592b75ccb 100644 --- a/assets/js/58574ab0.0202d015.js +++ b/assets/js/58574ab0.66ef880b.js @@ -1 +1 @@ -"use strict";(self.webpackChunkadminforth=self.webpackChunkadminforth||[]).push([[2487],{830:(e,n,t)=>{t.r(n),t.d(n,{assets:()=>l,contentTitle:()=>i,default:()=>p,frontMatter:()=>a,metadata:()=>o,toc:()=>d});var r=t(4848),s=t(8453);const a={},i="Getting Started",o={id:"gettingStarted",title:"Getting Started",description:"Prerequisites",source:"@site/docs/01-gettingStarted.md",sourceDirName:".",slug:"/gettingStarted",permalink:"/docs/gettingStarted",draft:!1,unlisted:!1,tags:[],version:"current",sidebarPosition:1,frontMatter:{},sidebar:"tutorialSidebar",next:{title:"Glossary",permalink:"/docs/glossary"}},l={},d=[{value:"Prerequisites",id:"prerequisites",level:2},{value:"Installation",id:"installation",level:2},{value:"Basic Philosophy",id:"basic-philosophy",level:2},{value:"Setting up a first demo",id:"setting-up-a-first-demo",level:2},{value:"Possible configuration options",id:"possible-configuration-options",level:2}];function c(e){const n={a:"a",code:"code",h1:"h1",h2:"h2",img:"img",p:"p",pre:"pre",...(0,s.R)(),...e.components};return(0,r.jsxs)(r.Fragment,{children:[(0,r.jsx)(n.h1,{id:"getting-started",children:"Getting Started"}),"\n",(0,r.jsx)(n.h2,{id:"prerequisites",children:"Prerequisites"}),"\n",(0,r.jsx)(n.p,{children:"We recommend using Node v18 and higher:"}),"\n",(0,r.jsx)(n.pre,{children:(0,r.jsx)(n.code,{className:"language-bash",children:"nvm install 18\nnvm alias default 18\nnvm use 18\n"})}),"\n",(0,r.jsx)(n.h2,{id:"installation",children:"Installation"}),"\n",(0,r.jsx)(n.pre,{children:(0,r.jsx)(n.code,{className:"language-bash",children:"mkdir myadmin\ncd myadmin\nnpm install adminforth\n"})}),"\n",(0,r.jsxs)(n.p,{children:["AdminForth does not provide own HTTP server, but can add own listeners over exisitng ",(0,r.jsx)(n.a,{href:"https://expressjs.com/",children:"Express"})," server (Fastify support is planned in future). This allows to create custom APIs for backoffice in a way you know."]}),"\n",(0,r.jsx)(n.p,{children:"Let's install express:"}),"\n",(0,r.jsx)(n.pre,{children:(0,r.jsx)(n.code,{className:"language-bash",children:"npm install express@4.19.2\n"})}),"\n",(0,r.jsx)(n.p,{children:"For demo purposes we will use SQLite data source. You can use postgress, Mongo or Clickhouse as well."}),"\n",(0,r.jsx)(n.pre,{children:(0,r.jsx)(n.code,{className:"language-bash",children:"npm install better-sqlite3@10.0.0\n"})}),"\n",(0,r.jsx)(n.p,{children:"You can use adminforth in pure Node, but we recommend using TypeScript for better development experience:"}),"\n",(0,r.jsx)(n.pre,{children:(0,r.jsx)(n.code,{className:"language-bash",children:"npm install typescript@5.4.5 tsx@4.11.2 --save-dev\n"})}),"\n",(0,r.jsx)(n.h2,{id:"basic-philosophy",children:"Basic Philosophy"}),"\n",(0,r.jsx)(n.p,{children:"AdminForth connects to existing databases and provides a backoffice for managing data including CRUD operations, filtering, sorting, and more."}),"\n",(0,r.jsx)(n.p,{children:"Database should be already created by using any database management tool, ORM or migrator. AdminForth does not provide a way to create tables or columns in the database."}),"\n",(0,r.jsx)(n.p,{children:'Once you have a database, you pass a connection string to AdminForth and define resources(tables) and columns you would like to see in backoffice. For most DB AdminForth can "discover" column types and constraints (e.g. max-length) by connecting to DB. However you can redefine them in AdminForth configuration. Type and constraints definition are take precedence over DB schema.'}),"\n",(0,r.jsx)(n.p,{children:'Also in AdminForth you can define in "Vue" way how each field will be rendered, and create own pages e.g. Dashboards.'}),"\n",(0,r.jsxs)(n.p,{children:["In the demo we will create a simple database with 2 tables: ",(0,r.jsx)(n.code,{children:"apartments"})," and ",(0,r.jsx)(n.code,{children:"users"}),". We will just use plain SQL to create tables and insert some fake data."]}),"\n",(0,r.jsx)(n.p,{children:"Users table will be used to store a credentials for login into backoffice itself."}),"\n",(0,r.jsx)(n.h2,{id:"setting-up-a-first-demo",children:"Setting up a first demo"}),"\n",(0,r.jsxs)(n.p,{children:["Open ",(0,r.jsx)(n.code,{children:"package.json"}),", set ",(0,r.jsx)(n.code,{children:"type"})," to ",(0,r.jsx)(n.code,{children:"module"})," and add ",(0,r.jsx)(n.code,{children:"start"})," script:"]}),"\n",(0,r.jsx)(n.pre,{children:(0,r.jsx)(n.code,{className:"language-json",children:'{\n ...\n "type": "module",\n "scripts": {\n ...\n "start": "ADMINFORTH_SECRET=CHANGE_ME_IN_PRODUCTION NODE_ENV=development tsx watch index.ts"\n },\n}\n'})}),"\n",(0,r.jsxs)(n.p,{children:["Create ",(0,r.jsx)(n.code,{children:"index.ts"})," file in root directory with following content:"]}),"\n",(0,r.jsx)(n.pre,{children:(0,r.jsx)(n.code,{className:"language-typescript",children:"\nimport betterSqlite3 from 'better-sqlite3';\nimport express from 'express';\nimport AdminForth from 'adminforth';\n\nconst dbFile = 'test.sqlite';\nconst db = betterSqlite3(dbFile)\n \nconst tableExists = db.prepare(`SELECT name FROM sqlite_master WHERE type='table' AND name='apartments';`).get();\nif (!tableExists) {\n await db.prepare(`\n CREATE TABLE apartments (\n id VARCHAR(20) PRIMARY KEY NOT NULL,\n title VARCHAR(255) NOT NULL,\n square_meter REAL,\n price DECIMAL(10, 2) NOT NULL,\n number_of_rooms INT,\n description TEXT,\n property_type VARCHAR(255) DEFAULT 'apartment',\n listed BOOLEAN DEFAULT FALSE,\n created_at TIMESTAMP,\n user_id VARCHAR(255)\n );`).run();\n\n await db.prepare(`\n CREATE TABLE users (\n id VARCHAR(255) PRIMARY KEY NOT NULL,\n email VARCHAR(255) NOT NULL,\n password_hash VARCHAR(255) NOT NULL,\n created_at VARCHAR(255) NOT NULL,\n role VARCHAR(255) NOT NULL\n );`).run();\n\n for (let i = 0; i < 50; i++) {\n await db.prepare(`\n INSERT INTO apartments (\n id, title, square_meter, price, number_of_rooms, description, created_at, listed, property_type\n ) VALUES ('${i}', 'Apartment ${i}', ${Math.random() * 100}, ${Math.random() * 10000}, ${Math\n .floor(Math.random() * 5) }, 'Next gen appartments', ${Date.now() / 1000 - i * 60 * 60 * 24}, ${i % 2 == 0}, ${i % 2 == 0 ? \"'house'\" : \"'apartment'\"});\n `).run();\n }\n}\n\nconst ADMIN_BASE_URL = '';\n\nconst admin = new AdminForth({\n baseUrl : ADMIN_BASE_URL,\n rootUser: {\n username: 'adminforth', // use these as credentials to login\n password: 'adminforth',\n },\n auth: {\n resourceId: 'users', // resource for getting user\n usernameField: 'email',\n passwordHashField: 'password_hash',\n },\n customization: {\n brandName: 'My Admin',\n datesFormat: 'D MMM YY HH:mm:ss',\n emptyFieldPlaceholder: '-',\n },\n\n dataSources: [\n {\n id: 'maindb',\n url: `sqlite://${dbFile}`\n },\n ],\n resources: [\n {\n dataSource: 'maindb', \n table: 'apartments',\n resourceId: 'apparts', // resourceId is defaulted to table name but you can change it e.g. \n // in case of same table names from different data sources\n label: 'Apartments', // label is defaulted to table name but you can change it\n recordLabel: (r) => `\ud83c\udfe1 ${r.title}`,\n columns: [\n { \n name: 'id', \n label: 'Identifier', // if you wish you can redefine label\n showIn: ['filter', 'show'], // show in filter and in show page\n primaryKey: true,\n fillOnCreate: ({initialRecord, adminUser}) => Math.random().toString(36).substring(7), // initialRecord is values user entered, adminUser object of user who creates record\n },\n { \n name: 'title',\n required: true,\n showIn: ['list', 'create', 'edit', 'filter', 'show'], // the default is full set\n maxLength: 255, // you can set max length for string fields\n minLength: 3, // you can set min length for string fields\n }, \n {\n name: 'created_at',\n type: AdminForth.Types.DATETIME ,\n allowMinMaxQuery: true,\n showIn: ['list', 'filter', 'show', 'edit'],\n fillOnCreate: ({initialRecord, adminUser}) => (new Date()).toISOString(),\n },\n { \n name: 'price',\n min: 10,\n max: 10000.12,\n allowMinMaxQuery: true, // use better experience for filtering e.g. date range, set it only if you have index on this column or if there will be low number of rows\n editingNote: 'Price is in USD', // you can appear note on editing or creating page\n },\n { \n name: 'square_meter', \n label: 'Square', \n allowMinMaxQuery: true,\n minValue: 1, // you can set min /max value for number fields\n maxValue: 1000,\n },\n { \n name: 'number_of_rooms',\n allowMinMaxQuery: true,\n enum: [\n { value: 1, label: '1 room' },\n { value: 2, label: '2 rooms' },\n { value: 3, label: '3 rooms' },\n { value: 4, label: '4 rooms' },\n { value: 5, label: '5 rooms' },\n ],\n },\n { \n name: 'description',\n sortable: false,\n },\n {\n name: 'property_type',\n enum: [{\n value: 'house',\n label: 'House'\n }, {\n value: 'apartment',\n label: 'Apartment'\n }, {\n value: null,\n label: 'Not defined'\n }],\n },\n {\n name: 'listed',\n required: true, // will be required on create/edit\n },\n {\n name: 'user_id',\n foreignResource: {\n resourceId: 'users',\n }\n }\n ],\n options: {\n listPageSize: 12,\n allowedActions:{\n edit: true,\n delete: true,\n show: true,\n filter: true,\n },\n },\n },\n { \n dataSource: 'maindb', \n table: 'users',\n resourceId: 'users',\n label: 'Users', \n recordLabel: (r) => `\ud83d\udc64 ${r.email}`,\n columns: [\n { \n name: 'id', \n primaryKey: true,\n fillOnCreate: ({initialRecord, adminUser}) => Math.random().toString(36).substring(7),\n showIn: ['list', 'filter', 'show'],\n },\n { \n name: 'email', \n required: true,\n isUnique: true,\n validation: [\n {\n regExp: '^[a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+\\\\.[a-zA-Z]{2,}$',\n message: 'Email is not valid, must be in format example@test.com'\n },\n ]\n },\n { \n name: 'created_at', \n type: AdminForth.Types.DATETIME,\n showIn: ['list', 'filter', 'show'],\n fillOnCreate: ({initialRecord, adminUser}) => (new Date()).toISOString(),\n },\n {\n name: 'role',\n enum: [\n { value: 'superadmin', label: 'Super Admin' },\n { value: 'user', label: 'User' },\n ]\n },\n {\n name: 'password',\n virtual: true, // field will not be persisted into db\n required: { create: true }, // make required only on create page\n editingNote: { edit: 'Leave empty to keep password unchanged' },\n minLength: 8,\n type: AdminForth.Types.STRING,\n showIn: ['create', 'edit'], // to show field only on create and edit pages\n masked: true, // to show stars in input field\n }\n ],\n hooks: {\n create: {\n beforeSave: async ({ record, adminUser, resource }) => {\n record.password_hash = await AdminForth.Utils.generatePasswordHash(record.password);\n return { ok:true, error: false };\n }\n },\n edit: {\n beforeSave: async ({ record, adminUser, resource}) => {\n if (record.password) {\n record.password_hash = await AdminForth.Utils.generatePasswordHash(record.password);\n }\n return { ok: true, error: false }\n },\n },\n }\n },\n ],\n menu: [\n {\n label: 'Core',\n icon: 'flowbite:brain-solid', // any icon from iconify supported in format :, e.g. from here https://icon-sets.iconify.design/flowbite/\n open: true,\n children: [\n {\n homepage: true,\n label: 'Appartments',\n icon: 'flowbite:home-solid',\n resourceId: 'apparts',\n },\n ]\n },\n {\n type: 'gap'\n },\n {\n type: 'divider'\n },\n {\n type: 'heading',\n label: 'SYSTEM',\n },\n {\n label: 'Users',\n icon: 'flowbite:user-solid',\n resourceId: 'users',\n }\n ],\n})\n\n\nconst app = express()\napp.use(express.json());\nconst port = 3500;\n\n(async () => {\n // needed to compile SPA. Call it here or from a build script e.g. in Docker build time to reduce downtime\n await admin.bundleNow({ hotReload: process.env.NODE_ENV === 'development'});\n console.log('Bundling AdminForth done. For faster serving consider calling bundleNow() from a build script.');\n})();\n\n\n// serve after you added all api\nadmin.express.serve(app, express)\nadmin.discoverDatabases();\n\n\napp.listen(port, () => {\n console.log(`Example app listening at http://localhost:${port}`)\n console.log(`\\n\u26a1 AdminForth is available at http://localhost:${port}${ADMIN_BASE_URL}\\n`)\n});\n"})}),"\n",(0,r.jsx)(n.p,{children:"Now you can run your app:"}),"\n",(0,r.jsx)(n.pre,{children:(0,r.jsx)(n.code,{className:"language-bash",children:"npm start\n"})}),"\n",(0,r.jsxs)(n.p,{children:["Open ",(0,r.jsx)(n.a,{href:"http://localhost:3500",children:"http://localhost:3500"})," in your browser and login with credentials ",(0,r.jsx)(n.code,{children:"adminforth"})," / ",(0,r.jsx)(n.code,{children:"adminforth"}),"."]}),"\n",(0,r.jsx)(n.p,{children:(0,r.jsx)(n.img,{alt:"alt text",src:t(8470).A+"",width:"3456",height:"2043"})}),"\n",(0,r.jsx)(n.p,{children:"After Login you should see:"}),"\n",(0,r.jsx)(n.h2,{id:"possible-configuration-options",children:"Possible configuration options"}),"\n",(0,r.jsx)(n.p,{children:"We will use schema with different column types for apartments to show many of AdminForth features."}),"\n",(0,r.jsxs)(n.p,{children:["Check ",(0,r.jsx)(n.a,{href:"/docs/api/types/AdminForthConfig/type-aliases/AdminForthConfig",children:"AdminForthConfig"})," for all possible options."]})]})}function p(e={}){const{wrapper:n}={...(0,s.R)(),...e.components};return n?(0,r.jsx)(n,{...e,children:(0,r.jsx)(c,{...e})}):c(e)}},8470:(e,n,t)=>{t.d(n,{A:()=>r});const r=t.p+"assets/images/image-4073119b1a64b412c6322c1917d972fc.png"},8453:(e,n,t)=>{t.d(n,{R:()=>i,x:()=>o});var r=t(6540);const s={},a=r.createContext(s);function i(e){const n=r.useContext(a);return r.useMemo((function(){return"function"==typeof e?e(n):{...n,...e}}),[n,e])}function o(e){let n;return n=e.disableParentContext?"function"==typeof e.components?e.components(s):e.components||s:i(e.components),r.createElement(a.Provider,{value:n},e.children)}}}]); \ No newline at end of file +"use strict";(self.webpackChunkadminforth=self.webpackChunkadminforth||[]).push([[2487],{830:(e,n,t)=>{t.r(n),t.d(n,{assets:()=>l,contentTitle:()=>i,default:()=>p,frontMatter:()=>a,metadata:()=>o,toc:()=>d});var r=t(4848),s=t(8453);const a={},i="Getting Started",o={id:"gettingStarted",title:"Getting Started",description:"Prerequisites",source:"@site/docs/01-gettingStarted.md",sourceDirName:".",slug:"/gettingStarted",permalink:"/docs/gettingStarted",draft:!1,unlisted:!1,tags:[],version:"current",sidebarPosition:1,frontMatter:{},sidebar:"tutorialSidebar",next:{title:"Glossary",permalink:"/docs/glossary"}},l={},d=[{value:"Prerequisites",id:"prerequisites",level:2},{value:"Installation",id:"installation",level:2},{value:"Basic Philosophy",id:"basic-philosophy",level:2},{value:"Setting up a first demo",id:"setting-up-a-first-demo",level:2},{value:"Possible configuration options",id:"possible-configuration-options",level:2}];function c(e){const n={a:"a",code:"code",h1:"h1",h2:"h2",img:"img",p:"p",pre:"pre",...(0,s.R)(),...e.components};return(0,r.jsxs)(r.Fragment,{children:[(0,r.jsx)(n.h1,{id:"getting-started",children:"Getting Started"}),"\n",(0,r.jsx)(n.h2,{id:"prerequisites",children:"Prerequisites"}),"\n",(0,r.jsx)(n.p,{children:"We recommend using Node v18 and higher:"}),"\n",(0,r.jsx)(n.pre,{children:(0,r.jsx)(n.code,{className:"language-bash",children:"nvm install 18\nnvm alias default 18\nnvm use 18\n"})}),"\n",(0,r.jsx)(n.h2,{id:"installation",children:"Installation"}),"\n",(0,r.jsx)(n.pre,{children:(0,r.jsx)(n.code,{className:"language-bash",children:"mkdir myadmin\ncd myadmin\nnpm install adminforth\n"})}),"\n",(0,r.jsxs)(n.p,{children:["AdminForth does not provide own HTTP server, but can add own listeners over exisitng ",(0,r.jsx)(n.a,{href:"https://expressjs.com/",children:"Express"})," server (Fastify support is planned in future). This allows to create custom APIs for backoffice in a way you know."]}),"\n",(0,r.jsx)(n.p,{children:"Let's install express:"}),"\n",(0,r.jsx)(n.pre,{children:(0,r.jsx)(n.code,{className:"language-bash",children:"npm install express@4.19.2\n"})}),"\n",(0,r.jsx)(n.p,{children:"For demo purposes we will use SQLite data source. You can use postgress, Mongo or Clickhouse as well."}),"\n",(0,r.jsx)(n.pre,{children:(0,r.jsx)(n.code,{className:"language-bash",children:"npm install better-sqlite3@10.0.0\n"})}),"\n",(0,r.jsx)(n.p,{children:"You can use adminforth in pure Node, but we recommend using TypeScript for better development experience:"}),"\n",(0,r.jsx)(n.pre,{children:(0,r.jsx)(n.code,{className:"language-bash",children:"npm install typescript@5.4.5 tsx@4.11.2 --save-dev\n"})}),"\n",(0,r.jsx)(n.h2,{id:"basic-philosophy",children:"Basic Philosophy"}),"\n",(0,r.jsx)(n.p,{children:"AdminForth connects to existing databases and provides a backoffice for managing data including CRUD operations, filtering, sorting, and more."}),"\n",(0,r.jsx)(n.p,{children:"Database should be already created by using any database management tool, ORM or migrator. AdminForth does not provide a way to create tables or columns in the database."}),"\n",(0,r.jsx)(n.p,{children:'Once you have a database, you pass a connection string to AdminForth and define resources(tables) and columns you would like to see in backoffice. For most DB AdminForth can "discover" column types and constraints (e.g. max-length) by connecting to DB. However you can redefine them in AdminForth configuration. Type and constraints definition are take precedence over DB schema.'}),"\n",(0,r.jsx)(n.p,{children:'Also in AdminForth you can define in "Vue" way how each field will be rendered, and create own pages e.g. Dashboards.'}),"\n",(0,r.jsxs)(n.p,{children:["In the demo we will create a simple database with 2 tables: ",(0,r.jsx)(n.code,{children:"apartments"})," and ",(0,r.jsx)(n.code,{children:"users"}),". We will just use plain SQL to create tables and insert some fake data."]}),"\n",(0,r.jsx)(n.p,{children:"Users table will be used to store a credentials for login into backoffice itself."}),"\n",(0,r.jsx)(n.h2,{id:"setting-up-a-first-demo",children:"Setting up a first demo"}),"\n",(0,r.jsxs)(n.p,{children:["Open ",(0,r.jsx)(n.code,{children:"package.json"}),", set ",(0,r.jsx)(n.code,{children:"type"})," to ",(0,r.jsx)(n.code,{children:"module"})," and add ",(0,r.jsx)(n.code,{children:"start"})," script:"]}),"\n",(0,r.jsx)(n.pre,{children:(0,r.jsx)(n.code,{className:"language-json",children:'{\n ...\n "type": "module",\n "scripts": {\n ...\n "start": "ADMINFORTH_SECRET=CHANGE_ME_IN_PRODUCTION NODE_ENV=development tsx watch index.ts"\n },\n}\n'})}),"\n",(0,r.jsxs)(n.p,{children:["Create ",(0,r.jsx)(n.code,{children:"index.ts"})," file in root directory with following content:"]}),"\n",(0,r.jsx)(n.pre,{children:(0,r.jsx)(n.code,{className:"language-typescript",children:"\nimport betterSqlite3 from 'better-sqlite3';\nimport express from 'express';\nimport AdminForth from 'adminforth';\n\nconst dbFile = 'test.sqlite';\nconst db = betterSqlite3(dbFile)\n \nconst tableExists = db.prepare(`SELECT name FROM sqlite_master WHERE type='table' AND name='apartments';`).get();\nif (!tableExists) {\n await db.prepare(`\n CREATE TABLE apartments (\n id VARCHAR(20) PRIMARY KEY NOT NULL,\n title VARCHAR(255) NOT NULL,\n square_meter REAL,\n price DECIMAL(10, 2) NOT NULL,\n number_of_rooms INT,\n description TEXT,\n property_type VARCHAR(255) DEFAULT 'apartment',\n listed BOOLEAN DEFAULT FALSE,\n created_at TIMESTAMP,\n user_id VARCHAR(255)\n );`).run();\n\n await db.prepare(`\n CREATE TABLE users (\n id VARCHAR(255) PRIMARY KEY NOT NULL,\n email VARCHAR(255) NOT NULL,\n password_hash VARCHAR(255) NOT NULL,\n created_at VARCHAR(255) NOT NULL,\n role VARCHAR(255) NOT NULL\n );`).run();\n\n for (let i = 0; i < 50; i++) {\n await db.prepare(`\n INSERT INTO apartments (\n id, title, square_meter, price, number_of_rooms, description, created_at, listed, property_type\n ) VALUES ('${i}', 'Apartment ${i}', ${Math.random() * 100}, ${Math.random() * 10000}, ${Math\n .floor(Math.random() * 5) }, 'Next gen appartments', ${Date.now() / 1000 - i * 60 * 60 * 24}, ${i % 2 == 0}, ${i % 2 == 0 ? \"'house'\" : \"'apartment'\"});\n `).run();\n }\n}\n\nconst ADMIN_BASE_URL = '';\n\nconst admin = new AdminForth({\n baseUrl : ADMIN_BASE_URL,\n rootUser: {\n username: 'adminforth', // use these as credentials to login\n password: 'adminforth',\n },\n auth: {\n resourceId: 'users', // resource for getting user\n usernameField: 'email',\n passwordHashField: 'password_hash',\n },\n customization: {\n brandName: 'My Admin',\n datesFormat: 'D MMM YY HH:mm:ss',\n emptyFieldPlaceholder: '-',\n },\n\n dataSources: [\n {\n id: 'maindb',\n url: `sqlite://${dbFile}`\n },\n ],\n resources: [\n {\n dataSource: 'maindb', \n table: 'apartments',\n resourceId: 'apparts', // resourceId is defaulted to table name but you can change it e.g. \n // in case of same table names from different data sources\n label: 'Apartments', // label is defaulted to table name but you can change it\n recordLabel: (r) => `\ud83c\udfe1 ${r.title}`,\n columns: [\n { \n name: 'id', \n label: 'Identifier', // if you wish you can redefine label\n showIn: ['filter', 'show'], // show in filter and in show page\n primaryKey: true,\n fillOnCreate: ({initialRecord, adminUser}) => Math.random().toString(36).substring(7), // initialRecord is values user entered, adminUser object of user who creates record\n },\n { \n name: 'title',\n required: true,\n showIn: ['list', 'create', 'edit', 'filter', 'show'], // the default is full set\n maxLength: 255, // you can set max length for string fields\n minLength: 3, // you can set min length for string fields\n }, \n {\n name: 'created_at',\n type: AdminForth.Types.DATETIME ,\n allowMinMaxQuery: true,\n showIn: ['list', 'filter', 'show', 'edit'],\n fillOnCreate: ({initialRecord, adminUser}) => (new Date()).toISOString(),\n },\n { \n name: 'price',\n min: 10,\n max: 10000.12,\n allowMinMaxQuery: true, // use better experience for filtering e.g. date range, set it only if you have index on this column or if there will be low number of rows\n editingNote: 'Price is in USD', // you can appear note on editing or creating page\n },\n { \n name: 'square_meter', \n label: 'Square', \n allowMinMaxQuery: true,\n minValue: 1, // you can set min /max value for number fields\n maxValue: 1000,\n },\n { \n name: 'number_of_rooms',\n allowMinMaxQuery: true,\n enum: [\n { value: 1, label: '1 room' },\n { value: 2, label: '2 rooms' },\n { value: 3, label: '3 rooms' },\n { value: 4, label: '4 rooms' },\n { value: 5, label: '5 rooms' },\n ],\n },\n { \n name: 'description',\n sortable: false,\n },\n {\n name: 'property_type',\n enum: [{\n value: 'house',\n label: 'House'\n }, {\n value: 'apartment',\n label: 'Apartment'\n }, {\n value: null,\n label: 'Not defined'\n }],\n },\n {\n name: 'listed',\n required: true, // will be required on create/edit\n },\n {\n name: 'user_id',\n foreignResource: {\n resourceId: 'users',\n }\n }\n ],\n options: {\n listPageSize: 12,\n allowedActions:{\n edit: true,\n delete: true,\n show: true,\n filter: true,\n },\n },\n },\n { \n dataSource: 'maindb', \n table: 'users',\n resourceId: 'users',\n label: 'Users', \n recordLabel: (r) => `\ud83d\udc64 ${r.email}`,\n columns: [\n { \n name: 'id', \n primaryKey: true,\n fillOnCreate: ({initialRecord, adminUser}) => Math.random().toString(36).substring(7),\n showIn: ['list', 'filter', 'show'],\n },\n { \n name: 'email', \n required: true,\n isUnique: true,\n validation: [\n {\n regExp: '^[a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+\\\\.[a-zA-Z]{2,}$',\n message: 'Email is not valid, must be in format example@test.com'\n },\n ]\n },\n { \n name: 'created_at', \n type: AdminForth.Types.DATETIME,\n showIn: ['list', 'filter', 'show'],\n fillOnCreate: ({initialRecord, adminUser}) => (new Date()).toISOString(),\n },\n {\n name: 'role',\n enum: [\n { value: 'superadmin', label: 'Super Admin' },\n { value: 'user', label: 'User' },\n ]\n },\n {\n name: 'password',\n virtual: true, // field will not be persisted into db\n required: { create: true }, // make required only on create page\n editingNote: { edit: 'Leave empty to keep password unchanged' },\n minLength: 8,\n type: AdminForth.Types.STRING,\n showIn: ['create', 'edit'], // to show field only on create and edit pages\n masked: true, // to show stars in input field\n }\n ],\n hooks: {\n create: {\n beforeSave: async ({ record, adminUser, resource }) => {\n record.password_hash = await AdminForth.Utils.generatePasswordHash(record.password);\n return { ok:true, error: false };\n }\n },\n edit: {\n beforeSave: async ({ record, adminUser, resource}) => {\n if (record.password) {\n record.password_hash = await AdminForth.Utils.generatePasswordHash(record.password);\n }\n return { ok: true, error: false }\n },\n },\n }\n },\n ],\n menu: [\n {\n label: 'Core',\n icon: 'flowbite:brain-solid', // any icon from iconify supported in format :, e.g. from here https://icon-sets.iconify.design/flowbite/\n open: true,\n children: [\n {\n homepage: true,\n label: 'Appartments',\n icon: 'flowbite:home-solid',\n resourceId: 'apparts',\n },\n ]\n },\n {\n type: 'gap'\n },\n {\n type: 'divider'\n },\n {\n type: 'heading',\n label: 'SYSTEM',\n },\n {\n label: 'Users',\n icon: 'flowbite:user-solid',\n resourceId: 'users',\n }\n ],\n})\n\n\nconst app = express()\napp.use(express.json());\nconst port = 3500;\n\n(async () => {\n // needed to compile SPA. Call it here or from a build script e.g. in Docker build time to reduce downtime\n await admin.bundleNow({ hotReload: process.env.NODE_ENV === 'development'});\n console.log('Bundling AdminForth done. For faster serving consider calling bundleNow() from a build script.');\n})();\n\n\n// serve after you added all api\nadmin.express.serve(app, express)\nadmin.discoverDatabases();\n\n\napp.listen(port, () => {\n console.log(`Example app listening at http://localhost:${port}`)\n console.log(`\\n\u26a1 AdminForth is available at http://localhost:${port}${ADMIN_BASE_URL}\\n`)\n});\n"})}),"\n",(0,r.jsx)(n.p,{children:"Now you can run your app:"}),"\n",(0,r.jsx)(n.pre,{children:(0,r.jsx)(n.code,{className:"language-bash",children:"npm start\n"})}),"\n",(0,r.jsxs)(n.p,{children:["Open ",(0,r.jsx)(n.a,{href:"http://localhost:3500",children:"http://localhost:3500"})," in your browser and login with credentials ",(0,r.jsx)(n.code,{children:"adminforth"})," / ",(0,r.jsx)(n.code,{children:"adminforth"}),"."]}),"\n",(0,r.jsx)(n.p,{children:(0,r.jsx)(n.img,{alt:"alt text",src:t(5529).A+"",width:"3456",height:"2043"})}),"\n",(0,r.jsx)(n.p,{children:"After Login you should see:"}),"\n",(0,r.jsx)(n.h2,{id:"possible-configuration-options",children:"Possible configuration options"}),"\n",(0,r.jsx)(n.p,{children:"We will use schema with different column types for apartments to show many of AdminForth features."}),"\n",(0,r.jsxs)(n.p,{children:["Check ",(0,r.jsx)(n.a,{href:"/docs/api/types/AdminForthConfig/type-aliases/AdminForthConfig",children:"AdminForthConfig"})," for all possible options."]})]})}function p(e={}){const{wrapper:n}={...(0,s.R)(),...e.components};return n?(0,r.jsx)(n,{...e,children:(0,r.jsx)(c,{...e})}):c(e)}},5529:(e,n,t)=>{t.d(n,{A:()=>r});const r=t.p+"assets/images/image-4073119b1a64b412c6322c1917d972fc.png"},8453:(e,n,t)=>{t.d(n,{R:()=>i,x:()=>o});var r=t(6540);const s={},a=r.createContext(s);function i(e){const n=r.useContext(a);return r.useMemo((function(){return"function"==typeof e?e(n):{...n,...e}}),[n,e])}function o(e){let n;return n=e.disableParentContext?"function"==typeof e.components?e.components(s):e.components||s:i(e.components),r.createElement(a.Provider,{value:n},e.children)}}}]); \ No newline at end of file diff --git a/assets/js/7661071f.bdcf50cf.js b/assets/js/7661071f.c8837661.js similarity index 96% rename from assets/js/7661071f.bdcf50cf.js rename to assets/js/7661071f.c8837661.js index 2c931a99a..60bb351dc 100644 --- a/assets/js/7661071f.bdcf50cf.js +++ b/assets/js/7661071f.c8837661.js @@ -1 +1 @@ -"use strict";(self.webpackChunkadminforth=self.webpackChunkadminforth||[]).push([[8737],{4137:(e,o,n)=>{n.r(o),n.d(o,{assets:()=>i,contentTitle:()=>l,default:()=>d,frontMatter:()=>r,metadata:()=>a,toc:()=>c});var s=n(4848),t=n(8453);const r={slug:"welcome",title:"Welcome",authors:["slorber","yangshun"],tags:["facebook","hello","docusaurus"]},l=void 0,a={permalink:"/blog/welcome",source:"@site/blog/2021-08-26-welcome/index.md",title:"Welcome",description:"Docusaurus blogging features are powered by the blog plugin.",date:"2021-08-26T00:00:00.000Z",tags:[{inline:!1,label:"Facebook",permalink:"/blog/tags/facebook",description:"Facebook tag description"},{inline:!1,label:"Hello",permalink:"/blog/tags/hello",description:"Hello tag description"},{inline:!1,label:"Docusaurus",permalink:"/blog/tags/docusaurus",description:"Docusaurus tag description"}],readingTime:.405,hasTruncateMarker:!1,authors:[{name:"S\xe9bastien Lorber",title:"Docusaurus maintainer",url:"https://sebastienlorber.com",imageURL:"https://github.com/slorber.png",key:"slorber"},{name:"Yangshun Tay",title:"Front End Engineer @ Facebook",url:"https://github.com/yangshun",imageURL:"https://github.com/yangshun.png",key:"yangshun"}],frontMatter:{slug:"welcome",title:"Welcome",authors:["slorber","yangshun"],tags:["facebook","hello","docusaurus"]},unlisted:!1,nextItem:{title:"MDX Blog Post",permalink:"/blog/mdx-blog-post"}},i={authorsImageUrls:[void 0,void 0]},c=[];function u(e){const o={a:"a",code:"code",img:"img",li:"li",p:"p",strong:"strong",ul:"ul",...(0,t.R)(),...e.components};return(0,s.jsxs)(s.Fragment,{children:[(0,s.jsxs)(o.p,{children:[(0,s.jsx)(o.a,{href:"https://docusaurus.io/docs/blog",children:"Docusaurus blogging features"})," are powered by the ",(0,s.jsx)(o.a,{href:"https://docusaurus.io/docs/api/plugins/@docusaurus/plugin-content-blog",children:"blog plugin"}),"."]}),"\n",(0,s.jsxs)(o.p,{children:["Simply add Markdown files (or folders) to the ",(0,s.jsx)(o.code,{children:"blog"})," directory."]}),"\n",(0,s.jsxs)(o.p,{children:["Regular blog authors can be added to ",(0,s.jsx)(o.code,{children:"authors.yml"}),"."]}),"\n",(0,s.jsx)(o.p,{children:"The blog post date can be extracted from filenames, such as:"}),"\n",(0,s.jsxs)(o.ul,{children:["\n",(0,s.jsx)(o.li,{children:(0,s.jsx)(o.code,{children:"2019-05-30-welcome.md"})}),"\n",(0,s.jsx)(o.li,{children:(0,s.jsx)(o.code,{children:"2019-05-30-welcome/index.md"})}),"\n"]}),"\n",(0,s.jsx)(o.p,{children:"A blog post folder can be convenient to co-locate blog post images:"}),"\n",(0,s.jsx)(o.p,{children:(0,s.jsx)(o.img,{alt:"Docusaurus Plushie",src:n(7450).A+"",width:"1500",height:"500"})}),"\n",(0,s.jsx)(o.p,{children:"The blog supports tags as well!"}),"\n",(0,s.jsxs)(o.p,{children:[(0,s.jsx)(o.strong,{children:"And if you don't want a blog"}),": just delete this directory, and use ",(0,s.jsx)(o.code,{children:"blog: false"})," in your Docusaurus config."]})]})}function d(e={}){const{wrapper:o}={...(0,t.R)(),...e.components};return o?(0,s.jsx)(o,{...e,children:(0,s.jsx)(u,{...e})}):u(e)}},7450:(e,o,n)=>{n.d(o,{A:()=>s});const s=n.p+"assets/images/docusaurus-plushie-banner-a60f7593abca1e3eef26a9afa244e4fb.jpeg"},8453:(e,o,n)=>{n.d(o,{R:()=>l,x:()=>a});var s=n(6540);const t={},r=s.createContext(t);function l(e){const o=s.useContext(r);return s.useMemo((function(){return"function"==typeof e?e(o):{...o,...e}}),[o,e])}function a(e){let o;return o=e.disableParentContext?"function"==typeof e.components?e.components(t):e.components||t:l(e.components),s.createElement(r.Provider,{value:o},e.children)}}}]); \ No newline at end of file +"use strict";(self.webpackChunkadminforth=self.webpackChunkadminforth||[]).push([[8737],{4137:(e,o,n)=>{n.r(o),n.d(o,{assets:()=>i,contentTitle:()=>l,default:()=>d,frontMatter:()=>r,metadata:()=>a,toc:()=>c});var s=n(4848),t=n(8453);const r={slug:"welcome",title:"Welcome",authors:["slorber","yangshun"],tags:["facebook","hello","docusaurus"]},l=void 0,a={permalink:"/blog/welcome",source:"@site/blog/2021-08-26-welcome/index.md",title:"Welcome",description:"Docusaurus blogging features are powered by the blog plugin.",date:"2021-08-26T00:00:00.000Z",tags:[{inline:!1,label:"Facebook",permalink:"/blog/tags/facebook",description:"Facebook tag description"},{inline:!1,label:"Hello",permalink:"/blog/tags/hello",description:"Hello tag description"},{inline:!1,label:"Docusaurus",permalink:"/blog/tags/docusaurus",description:"Docusaurus tag description"}],readingTime:.405,hasTruncateMarker:!1,authors:[{name:"S\xe9bastien Lorber",title:"Docusaurus maintainer",url:"https://sebastienlorber.com",imageURL:"https://github.com/slorber.png",key:"slorber"},{name:"Yangshun Tay",title:"Front End Engineer @ Facebook",url:"https://github.com/yangshun",imageURL:"https://github.com/yangshun.png",key:"yangshun"}],frontMatter:{slug:"welcome",title:"Welcome",authors:["slorber","yangshun"],tags:["facebook","hello","docusaurus"]},unlisted:!1,nextItem:{title:"MDX Blog Post",permalink:"/blog/mdx-blog-post"}},i={authorsImageUrls:[void 0,void 0]},c=[];function u(e){const o={a:"a",code:"code",img:"img",li:"li",p:"p",strong:"strong",ul:"ul",...(0,t.R)(),...e.components};return(0,s.jsxs)(s.Fragment,{children:[(0,s.jsxs)(o.p,{children:[(0,s.jsx)(o.a,{href:"https://docusaurus.io/docs/blog",children:"Docusaurus blogging features"})," are powered by the ",(0,s.jsx)(o.a,{href:"https://docusaurus.io/docs/api/plugins/@docusaurus/plugin-content-blog",children:"blog plugin"}),"."]}),"\n",(0,s.jsxs)(o.p,{children:["Simply add Markdown files (or folders) to the ",(0,s.jsx)(o.code,{children:"blog"})," directory."]}),"\n",(0,s.jsxs)(o.p,{children:["Regular blog authors can be added to ",(0,s.jsx)(o.code,{children:"authors.yml"}),"."]}),"\n",(0,s.jsx)(o.p,{children:"The blog post date can be extracted from filenames, such as:"}),"\n",(0,s.jsxs)(o.ul,{children:["\n",(0,s.jsx)(o.li,{children:(0,s.jsx)(o.code,{children:"2019-05-30-welcome.md"})}),"\n",(0,s.jsx)(o.li,{children:(0,s.jsx)(o.code,{children:"2019-05-30-welcome/index.md"})}),"\n"]}),"\n",(0,s.jsx)(o.p,{children:"A blog post folder can be convenient to co-locate blog post images:"}),"\n",(0,s.jsx)(o.p,{children:(0,s.jsx)(o.img,{alt:"Docusaurus Plushie",src:n(2793).A+"",width:"1500",height:"500"})}),"\n",(0,s.jsx)(o.p,{children:"The blog supports tags as well!"}),"\n",(0,s.jsxs)(o.p,{children:[(0,s.jsx)(o.strong,{children:"And if you don't want a blog"}),": just delete this directory, and use ",(0,s.jsx)(o.code,{children:"blog: false"})," in your Docusaurus config."]})]})}function d(e={}){const{wrapper:o}={...(0,t.R)(),...e.components};return o?(0,s.jsx)(o,{...e,children:(0,s.jsx)(u,{...e})}):u(e)}},2793:(e,o,n)=>{n.d(o,{A:()=>s});const s=n.p+"assets/images/docusaurus-plushie-banner-a60f7593abca1e3eef26a9afa244e4fb.jpeg"},8453:(e,o,n)=>{n.d(o,{R:()=>l,x:()=>a});var s=n(6540);const t={},r=s.createContext(t);function l(e){const o=s.useContext(r);return s.useMemo((function(){return"function"==typeof e?e(o):{...o,...e}}),[o,e])}function a(e){let o;return o=e.disableParentContext?"function"==typeof e.components?e.components(t):e.components||t:l(e.components),s.createElement(r.Provider,{value:o},e.children)}}}]); \ No newline at end of file diff --git a/assets/js/9c3f971e.874bcd17.js b/assets/js/9c3f971e.9a6788a7.js similarity index 89% rename from assets/js/9c3f971e.874bcd17.js rename to assets/js/9c3f971e.9a6788a7.js index fec39ea42..a0e308a66 100644 --- a/assets/js/9c3f971e.874bcd17.js +++ b/assets/js/9c3f971e.9a6788a7.js @@ -1 +1 @@ -"use strict";(self.webpackChunkadminforth=self.webpackChunkadminforth||[]).push([[510],{4060:(e,n,s)=>{s.r(n),s.d(n,{assets:()=>d,contentTitle:()=>i,default:()=>u,frontMatter:()=>r,metadata:()=>a,toc:()=>c});var o=s(4848),t=s(8453);const r={},i="Customization",a={id:"customization",title:"Customization",description:"Here is how you can customize the AdminForth to fit your needs.",source:"@site/docs/03-customization.md",sourceDirName:".",slug:"/customization",permalink:"/docs/customization",draft:!1,unlisted:!1,tags:[],version:"current",sidebarPosition:3,frontMatter:{},sidebar:"tutorialSidebar",previous:{title:"Glossary",permalink:"/docs/glossary"},next:{title:"AuditLog",permalink:"/docs/Plugins/AuditLog"}},d={},c=[{value:"Customizing how AdminForth renders the cells with record values",id:"customizing-how-adminforth-renders-the-cells-with-record-values",level:2},{value:"Hooks",id:"hooks",level:2},{value:"Modify the data before it is saved to the database",id:"modify-the-data-before-it-is-saved-to-the-database",level:3},{value:"Limiting access to the resource actions",id:"limiting-access-to-the-resource-actions",level:2},{value:"Statically disable some action",id:"statically-disable-some-action",level:3},{value:"Disable some action based on logged in user record or role",id:"disable-some-action-based-on-logged-in-user-record-or-role",level:3},{value:"Reuse the same callback for multiple actions",id:"reuse-the-same-callback-for-multiple-actions",level:3},{value:"Customizing the access control based on resource values",id:"customizing-the-access-control-based-on-resource-values",level:3}];function l(e){const n={a:"a",blockquote:"blockquote",code:"code",h1:"h1",h2:"h2",h3:"h3",li:"li",p:"p",pre:"pre",ul:"ul",...(0,t.R)(),...e.components};return(0,o.jsxs)(o.Fragment,{children:[(0,o.jsx)(n.h1,{id:"customization",children:"Customization"}),"\n",(0,o.jsx)(n.p,{children:"Here is how you can customize the AdminForth to fit your needs."}),"\n",(0,o.jsx)(n.h2,{id:"customizing-how-adminforth-renders-the-cells-with-record-values",children:"Customizing how AdminForth renders the cells with record values"}),"\n",(0,o.jsxs)(n.p,{children:["Let's change how AdminForth renders the number of rooms in the 'list' and 'show' views.\nWe will render '\ud83d\udfe8' for each room and then we will print ",(0,o.jsx)(n.code,{children:"square_meter"})," at the same cells."]}),"\n",(0,o.jsxs)(n.p,{children:["Create directory ",(0,o.jsx)(n.code,{children:"custom"}),". Create a file ",(0,o.jsx)(n.code,{children:"RoomsCell.vue"})," in it:"]}),"\n",(0,o.jsx)(n.pre,{children:(0,o.jsx)(n.code,{className:"language-vue",children:'\n\n + diff --git a/blog/first-blog-post/index.html b/blog/first-blog-post/index.html index 180a82703..fe716c944 100644 --- a/blog/first-blog-post/index.html +++ b/blog/first-blog-post/index.html @@ -5,7 +5,7 @@ First Blog Post | AdminForth - + diff --git a/blog/index.html b/blog/index.html index 4ff7a84c1..fba393daa 100644 --- a/blog/index.html +++ b/blog/index.html @@ -5,7 +5,7 @@ Blog | AdminForth - + diff --git a/blog/long-blog-post/index.html b/blog/long-blog-post/index.html index 7b513bb66..d038ca9ab 100644 --- a/blog/long-blog-post/index.html +++ b/blog/long-blog-post/index.html @@ -5,7 +5,7 @@ Long Blog Post | AdminForth - + diff --git a/blog/mdx-blog-post/index.html b/blog/mdx-blog-post/index.html index 70a8affc4..8062185a4 100644 --- a/blog/mdx-blog-post/index.html +++ b/blog/mdx-blog-post/index.html @@ -5,7 +5,7 @@ MDX Blog Post | AdminForth - + diff --git a/blog/tags/docusaurus/index.html b/blog/tags/docusaurus/index.html index 7e9430460..40d9568b2 100644 --- a/blog/tags/docusaurus/index.html +++ b/blog/tags/docusaurus/index.html @@ -5,7 +5,7 @@ 4 posts tagged with "Docusaurus" | AdminForth - + diff --git a/blog/tags/facebook/index.html b/blog/tags/facebook/index.html index fb038b279..c584cee92 100644 --- a/blog/tags/facebook/index.html +++ b/blog/tags/facebook/index.html @@ -5,7 +5,7 @@ One post tagged with "Facebook" | AdminForth - + diff --git a/blog/tags/hello/index.html b/blog/tags/hello/index.html index 0b8aad00f..5693cf89a 100644 --- a/blog/tags/hello/index.html +++ b/blog/tags/hello/index.html @@ -5,7 +5,7 @@ 2 posts tagged with "Hello" | AdminForth - + diff --git a/blog/tags/hola/index.html b/blog/tags/hola/index.html index 966935570..5277b67fb 100644 --- a/blog/tags/hola/index.html +++ b/blog/tags/hola/index.html @@ -5,7 +5,7 @@ One post tagged with "Hola" | AdminForth - + diff --git a/blog/tags/index.html b/blog/tags/index.html index b1dc70f5f..16c17ea23 100644 --- a/blog/tags/index.html +++ b/blog/tags/index.html @@ -5,7 +5,7 @@ Tags | AdminForth - + diff --git a/blog/welcome/index.html b/blog/welcome/index.html index bd10633c2..564edacfe 100644 --- a/blog/welcome/index.html +++ b/blog/welcome/index.html @@ -5,7 +5,7 @@ Welcome | AdminForth - + diff --git a/docs/Plugins/AuditLog/index.html b/docs/Plugins/AuditLog/index.html index 235674f62..82778ba04 100644 --- a/docs/Plugins/AuditLog/index.html +++ b/docs/Plugins/AuditLog/index.html @@ -5,7 +5,7 @@ AuditLog | AdminForth - + diff --git a/docs/Plugins/ForeignInlineList/index.html b/docs/Plugins/ForeignInlineList/index.html index 497ee14be..4d23e9631 100644 --- a/docs/Plugins/ForeignInlineList/index.html +++ b/docs/Plugins/ForeignInlineList/index.html @@ -5,7 +5,7 @@ ForeignInlineList | AdminForth - + diff --git a/docs/api/index.html b/docs/api/index.html index 2fa67e844..32ea7f6d9 100644 --- a/docs/api/index.html +++ b/docs/api/index.html @@ -5,7 +5,7 @@ TypeDoc API | AdminForth - + diff --git a/docs/api/plugins/AuditLog/types/index.html b/docs/api/plugins/AuditLog/types/index.html index fb7409859..d907aa5ea 100644 --- a/docs/api/plugins/AuditLog/types/index.html +++ b/docs/api/plugins/AuditLog/types/index.html @@ -5,7 +5,7 @@ plugins/AuditLog/types | AdminForth - + diff --git a/docs/api/plugins/AuditLog/types/type-aliases/PluginOptions/index.html b/docs/api/plugins/AuditLog/types/type-aliases/PluginOptions/index.html index 44c733eec..fa860a61d 100644 --- a/docs/api/plugins/AuditLog/types/type-aliases/PluginOptions/index.html +++ b/docs/api/plugins/AuditLog/types/type-aliases/PluginOptions/index.html @@ -5,7 +5,7 @@ PluginOptions | AdminForth - + diff --git a/docs/api/plugins/ForeignInlineListPlugin/types/index.html b/docs/api/plugins/ForeignInlineListPlugin/types/index.html index 70a15b34a..927d34f0f 100644 --- a/docs/api/plugins/ForeignInlineListPlugin/types/index.html +++ b/docs/api/plugins/ForeignInlineListPlugin/types/index.html @@ -5,7 +5,7 @@ plugins/ForeignInlineListPlugin/types | AdminForth - + diff --git a/docs/api/plugins/ForeignInlineListPlugin/types/type-aliases/PluginOptions/index.html b/docs/api/plugins/ForeignInlineListPlugin/types/type-aliases/PluginOptions/index.html index 793829fef..5d5d333ff 100644 --- a/docs/api/plugins/ForeignInlineListPlugin/types/type-aliases/PluginOptions/index.html +++ b/docs/api/plugins/ForeignInlineListPlugin/types/type-aliases/PluginOptions/index.html @@ -5,7 +5,7 @@ PluginOptions | AdminForth - + diff --git a/docs/api/types/AdminForthConfig/enumerations/ActionCheckSource/index.html b/docs/api/types/AdminForthConfig/enumerations/ActionCheckSource/index.html index b01c8392e..30a7758e7 100644 --- a/docs/api/types/AdminForthConfig/enumerations/ActionCheckSource/index.html +++ b/docs/api/types/AdminForthConfig/enumerations/ActionCheckSource/index.html @@ -5,7 +5,7 @@ ActionCheckSource | AdminForth - + diff --git a/docs/api/types/AdminForthConfig/enumerations/AdminForthDataTypes/index.html b/docs/api/types/AdminForthConfig/enumerations/AdminForthDataTypes/index.html index eee8214cb..0aadae0e6 100644 --- a/docs/api/types/AdminForthConfig/enumerations/AdminForthDataTypes/index.html +++ b/docs/api/types/AdminForthConfig/enumerations/AdminForthDataTypes/index.html @@ -5,7 +5,7 @@ AdminForthDataTypes | AdminForth - + diff --git a/docs/api/types/AdminForthConfig/enumerations/AdminForthFilterOperators/index.html b/docs/api/types/AdminForthConfig/enumerations/AdminForthFilterOperators/index.html index 54e2a465f..112474303 100644 --- a/docs/api/types/AdminForthConfig/enumerations/AdminForthFilterOperators/index.html +++ b/docs/api/types/AdminForthConfig/enumerations/AdminForthFilterOperators/index.html @@ -5,7 +5,7 @@ AdminForthFilterOperators | AdminForth - + diff --git a/docs/api/types/AdminForthConfig/enumerations/AdminForthMenuTypes/index.html b/docs/api/types/AdminForthConfig/enumerations/AdminForthMenuTypes/index.html index af8d4de19..92bd26bcd 100644 --- a/docs/api/types/AdminForthConfig/enumerations/AdminForthMenuTypes/index.html +++ b/docs/api/types/AdminForthConfig/enumerations/AdminForthMenuTypes/index.html @@ -5,7 +5,7 @@ AdminForthMenuTypes | AdminForth - + diff --git a/docs/api/types/AdminForthConfig/enumerations/AdminForthResourcePages/index.html b/docs/api/types/AdminForthConfig/enumerations/AdminForthResourcePages/index.html index c0c939781..82d0c18de 100644 --- a/docs/api/types/AdminForthConfig/enumerations/AdminForthResourcePages/index.html +++ b/docs/api/types/AdminForthConfig/enumerations/AdminForthResourcePages/index.html @@ -5,7 +5,7 @@ AdminForthResourcePages | AdminForth - + diff --git a/docs/api/types/AdminForthConfig/enumerations/AdminForthSortDirections/index.html b/docs/api/types/AdminForthConfig/enumerations/AdminForthSortDirections/index.html index 2a45a09d3..e3de03209 100644 --- a/docs/api/types/AdminForthConfig/enumerations/AdminForthSortDirections/index.html +++ b/docs/api/types/AdminForthConfig/enumerations/AdminForthSortDirections/index.html @@ -5,7 +5,7 @@ AdminForthSortDirections | AdminForth - + diff --git a/docs/api/types/AdminForthConfig/enumerations/AllowedActionsEnum/index.html b/docs/api/types/AdminForthConfig/enumerations/AllowedActionsEnum/index.html index 55163d19f..f8639bff7 100644 --- a/docs/api/types/AdminForthConfig/enumerations/AllowedActionsEnum/index.html +++ b/docs/api/types/AdminForthConfig/enumerations/AllowedActionsEnum/index.html @@ -5,7 +5,7 @@ AllowedActionsEnum | AdminForth - + diff --git a/docs/api/types/AdminForthConfig/index.html b/docs/api/types/AdminForthConfig/index.html index 9c7cf70da..c1f1f4f31 100644 --- a/docs/api/types/AdminForthConfig/index.html +++ b/docs/api/types/AdminForthConfig/index.html @@ -5,7 +5,7 @@ types/AdminForthConfig | AdminForth - + diff --git a/docs/api/types/AdminForthConfig/interfaces/AdminForthClass/index.html b/docs/api/types/AdminForthConfig/interfaces/AdminForthClass/index.html index 2f0944190..37180369b 100644 --- a/docs/api/types/AdminForthConfig/interfaces/AdminForthClass/index.html +++ b/docs/api/types/AdminForthConfig/interfaces/AdminForthClass/index.html @@ -5,7 +5,7 @@ AdminForthClass | AdminForth - + diff --git a/docs/api/types/AdminForthConfig/interfaces/AdminForthPluginType/index.html b/docs/api/types/AdminForthConfig/interfaces/AdminForthPluginType/index.html index f353a8752..9e206301c 100644 --- a/docs/api/types/AdminForthConfig/interfaces/AdminForthPluginType/index.html +++ b/docs/api/types/AdminForthConfig/interfaces/AdminForthPluginType/index.html @@ -5,7 +5,7 @@ AdminForthPluginType | AdminForth - + diff --git a/docs/api/types/AdminForthConfig/interfaces/CodeInjectorType/index.html b/docs/api/types/AdminForthConfig/interfaces/CodeInjectorType/index.html index d603d52e9..5c2e974fa 100644 --- a/docs/api/types/AdminForthConfig/interfaces/CodeInjectorType/index.html +++ b/docs/api/types/AdminForthConfig/interfaces/CodeInjectorType/index.html @@ -5,7 +5,7 @@ CodeInjectorType | AdminForth - + diff --git a/docs/api/types/AdminForthConfig/interfaces/ExpressHttpServer/index.html b/docs/api/types/AdminForthConfig/interfaces/ExpressHttpServer/index.html index 7b150697e..3666ea79b 100644 --- a/docs/api/types/AdminForthConfig/interfaces/ExpressHttpServer/index.html +++ b/docs/api/types/AdminForthConfig/interfaces/ExpressHttpServer/index.html @@ -5,7 +5,7 @@ ExpressHttpServer | AdminForth - + diff --git a/docs/api/types/AdminForthConfig/interfaces/GenericHttpServer/index.html b/docs/api/types/AdminForthConfig/interfaces/GenericHttpServer/index.html index b64ba4444..dcaf8c158 100644 --- a/docs/api/types/AdminForthConfig/interfaces/GenericHttpServer/index.html +++ b/docs/api/types/AdminForthConfig/interfaces/GenericHttpServer/index.html @@ -5,7 +5,7 @@ GenericHttpServer | AdminForth - + diff --git a/docs/api/types/AdminForthConfig/type-aliases/AdminForthColumnEnumItem/index.html b/docs/api/types/AdminForthConfig/type-aliases/AdminForthColumnEnumItem/index.html index 022605340..45b00259a 100644 --- a/docs/api/types/AdminForthConfig/type-aliases/AdminForthColumnEnumItem/index.html +++ b/docs/api/types/AdminForthConfig/type-aliases/AdminForthColumnEnumItem/index.html @@ -5,7 +5,7 @@ AdminForthColumnEnumItem | AdminForth - + diff --git a/docs/api/types/AdminForthConfig/type-aliases/AdminForthComponentDeclaration/index.html b/docs/api/types/AdminForthConfig/type-aliases/AdminForthComponentDeclaration/index.html index 167c6be82..0e259db68 100644 --- a/docs/api/types/AdminForthConfig/type-aliases/AdminForthComponentDeclaration/index.html +++ b/docs/api/types/AdminForthConfig/type-aliases/AdminForthComponentDeclaration/index.html @@ -5,7 +5,7 @@ AdminForthComponentDeclaration | AdminForth - + diff --git a/docs/api/types/AdminForthConfig/type-aliases/AdminForthComponentDeclarationFull/index.html b/docs/api/types/AdminForthConfig/type-aliases/AdminForthComponentDeclarationFull/index.html index c378d275f..4566c5d1c 100644 --- a/docs/api/types/AdminForthConfig/type-aliases/AdminForthComponentDeclarationFull/index.html +++ b/docs/api/types/AdminForthConfig/type-aliases/AdminForthComponentDeclarationFull/index.html @@ -5,7 +5,7 @@ AdminForthComponentDeclarationFull | AdminForth - + diff --git a/docs/api/types/AdminForthConfig/type-aliases/AdminForthConfig/index.html b/docs/api/types/AdminForthConfig/type-aliases/AdminForthConfig/index.html index 3857a4801..3e541021d 100644 --- a/docs/api/types/AdminForthConfig/type-aliases/AdminForthConfig/index.html +++ b/docs/api/types/AdminForthConfig/type-aliases/AdminForthConfig/index.html @@ -5,7 +5,7 @@ AdminForthConfig | AdminForth - + diff --git a/docs/api/types/AdminForthConfig/type-aliases/AdminForthConfigMenuItem/index.html b/docs/api/types/AdminForthConfig/type-aliases/AdminForthConfigMenuItem/index.html index eb84183f2..5a74b83a2 100644 --- a/docs/api/types/AdminForthConfig/type-aliases/AdminForthConfigMenuItem/index.html +++ b/docs/api/types/AdminForthConfig/type-aliases/AdminForthConfigMenuItem/index.html @@ -5,7 +5,7 @@ AdminForthConfigMenuItem | AdminForth - + diff --git a/docs/api/types/AdminForthConfig/type-aliases/AdminForthDataSource/index.html b/docs/api/types/AdminForthConfig/type-aliases/AdminForthDataSource/index.html index c34e841d8..eca8f9615 100644 --- a/docs/api/types/AdminForthConfig/type-aliases/AdminForthDataSource/index.html +++ b/docs/api/types/AdminForthConfig/type-aliases/AdminForthDataSource/index.html @@ -5,7 +5,7 @@ AdminForthDataSource | AdminForth - + diff --git a/docs/api/types/AdminForthConfig/type-aliases/AdminForthFieldComponents/index.html b/docs/api/types/AdminForthConfig/type-aliases/AdminForthFieldComponents/index.html index 81da521a9..ab8c71064 100644 --- a/docs/api/types/AdminForthConfig/type-aliases/AdminForthFieldComponents/index.html +++ b/docs/api/types/AdminForthConfig/type-aliases/AdminForthFieldComponents/index.html @@ -5,7 +5,7 @@ AdminForthFieldComponents | AdminForth - + diff --git a/docs/api/types/AdminForthConfig/type-aliases/AdminForthForeignResource/index.html b/docs/api/types/AdminForthConfig/type-aliases/AdminForthForeignResource/index.html index 824e76fdc..78d10e69c 100644 --- a/docs/api/types/AdminForthConfig/type-aliases/AdminForthForeignResource/index.html +++ b/docs/api/types/AdminForthConfig/type-aliases/AdminForthForeignResource/index.html @@ -5,7 +5,7 @@ AdminForthForeignResource | AdminForth - + diff --git a/docs/api/types/AdminForthConfig/type-aliases/AdminForthResource/index.html b/docs/api/types/AdminForthConfig/type-aliases/AdminForthResource/index.html index 2560ebd2c..80c9c5d03 100644 --- a/docs/api/types/AdminForthConfig/type-aliases/AdminForthResource/index.html +++ b/docs/api/types/AdminForthConfig/type-aliases/AdminForthResource/index.html @@ -5,7 +5,7 @@ AdminForthResource | AdminForth - + diff --git a/docs/api/types/AdminForthConfig/type-aliases/AdminForthResourceColumn/index.html b/docs/api/types/AdminForthConfig/type-aliases/AdminForthResourceColumn/index.html index 034fc5dc7..9afea00c4 100644 --- a/docs/api/types/AdminForthConfig/type-aliases/AdminForthResourceColumn/index.html +++ b/docs/api/types/AdminForthConfig/type-aliases/AdminForthResourceColumn/index.html @@ -5,7 +5,7 @@ AdminForthResourceColumn | AdminForth - + diff --git a/docs/api/types/AdminForthConfig/type-aliases/AdminUser/index.html b/docs/api/types/AdminForthConfig/type-aliases/AdminUser/index.html index 8203fa623..9a0621bee 100644 --- a/docs/api/types/AdminForthConfig/type-aliases/AdminUser/index.html +++ b/docs/api/types/AdminForthConfig/type-aliases/AdminUser/index.html @@ -5,7 +5,7 @@ AdminUser | AdminForth - + diff --git a/docs/api/types/AdminForthConfig/type-aliases/AfterDataSourceResponseFunction/index.html b/docs/api/types/AdminForthConfig/type-aliases/AfterDataSourceResponseFunction/index.html index 4877d6187..b959920a9 100644 --- a/docs/api/types/AdminForthConfig/type-aliases/AfterDataSourceResponseFunction/index.html +++ b/docs/api/types/AdminForthConfig/type-aliases/AfterDataSourceResponseFunction/index.html @@ -5,7 +5,7 @@ AfterDataSourceResponseFunction() | AdminForth - + diff --git a/docs/api/types/AdminForthConfig/type-aliases/AfterSaveFunction/index.html b/docs/api/types/AdminForthConfig/type-aliases/AfterSaveFunction/index.html index d48084d76..0c543084b 100644 --- a/docs/api/types/AdminForthConfig/type-aliases/AfterSaveFunction/index.html +++ b/docs/api/types/AdminForthConfig/type-aliases/AfterSaveFunction/index.html @@ -5,7 +5,7 @@ AfterSaveFunction() | AdminForth - + diff --git a/docs/api/types/AdminForthConfig/type-aliases/AllowedActionValue/index.html b/docs/api/types/AdminForthConfig/type-aliases/AllowedActionValue/index.html index 26c5604e0..f587e4e51 100644 --- a/docs/api/types/AdminForthConfig/type-aliases/AllowedActionValue/index.html +++ b/docs/api/types/AdminForthConfig/type-aliases/AllowedActionValue/index.html @@ -5,7 +5,7 @@ AllowedActionValue | AdminForth - + diff --git a/docs/api/types/AdminForthConfig/type-aliases/AllowedActions/index.html b/docs/api/types/AdminForthConfig/type-aliases/AllowedActions/index.html index bb9fb95ae..613c0caec 100644 --- a/docs/api/types/AdminForthConfig/type-aliases/AllowedActions/index.html +++ b/docs/api/types/AdminForthConfig/type-aliases/AllowedActions/index.html @@ -5,7 +5,7 @@ AllowedActions | AdminForth - + diff --git a/docs/api/types/AdminForthConfig/type-aliases/BeforeDataSourceRequestFunction/index.html b/docs/api/types/AdminForthConfig/type-aliases/BeforeDataSourceRequestFunction/index.html index fe70638e0..bbf4fe490 100644 --- a/docs/api/types/AdminForthConfig/type-aliases/BeforeDataSourceRequestFunction/index.html +++ b/docs/api/types/AdminForthConfig/type-aliases/BeforeDataSourceRequestFunction/index.html @@ -5,7 +5,7 @@ BeforeDataSourceRequestFunction() | AdminForth - + diff --git a/docs/api/types/AdminForthConfig/type-aliases/BeforeSaveFunction/index.html b/docs/api/types/AdminForthConfig/type-aliases/BeforeSaveFunction/index.html index 9dd2c0431..866a042ef 100644 --- a/docs/api/types/AdminForthConfig/type-aliases/BeforeSaveFunction/index.html +++ b/docs/api/types/AdminForthConfig/type-aliases/BeforeSaveFunction/index.html @@ -5,7 +5,7 @@ BeforeSaveFunction() | AdminForth - + diff --git a/docs/api/types/AdminForthConfig/type-aliases/ValidationObject/index.html b/docs/api/types/AdminForthConfig/type-aliases/ValidationObject/index.html index 143e8bbbc..31aba2f4b 100644 --- a/docs/api/types/AdminForthConfig/type-aliases/ValidationObject/index.html +++ b/docs/api/types/AdminForthConfig/type-aliases/ValidationObject/index.html @@ -5,7 +5,7 @@ ValidationObject | AdminForth - + diff --git a/docs/api/types/FrontendAPI/enumerations/AlertVariant/index.html b/docs/api/types/FrontendAPI/enumerations/AlertVariant/index.html index 7650cf81e..2ae7ccff7 100644 --- a/docs/api/types/FrontendAPI/enumerations/AlertVariant/index.html +++ b/docs/api/types/FrontendAPI/enumerations/AlertVariant/index.html @@ -5,7 +5,7 @@ AlertVariant | AdminForth - + diff --git a/docs/api/types/FrontendAPI/index.html b/docs/api/types/FrontendAPI/index.html index 3eec37abc..dc963a0fd 100644 --- a/docs/api/types/FrontendAPI/index.html +++ b/docs/api/types/FrontendAPI/index.html @@ -5,7 +5,7 @@ types/FrontendAPI | AdminForth - + diff --git a/docs/api/types/FrontendAPI/interfaces/FrontendAPIInterface/index.html b/docs/api/types/FrontendAPI/interfaces/FrontendAPIInterface/index.html index 48720978d..06b6dc074 100644 --- a/docs/api/types/FrontendAPI/interfaces/FrontendAPIInterface/index.html +++ b/docs/api/types/FrontendAPI/interfaces/FrontendAPIInterface/index.html @@ -5,7 +5,7 @@ FrontendAPIInterface | AdminForth - + diff --git a/docs/api/types/FrontendAPI/type-aliases/AlertParams/index.html b/docs/api/types/FrontendAPI/type-aliases/AlertParams/index.html index b95c0e16a..11f53904a 100644 --- a/docs/api/types/FrontendAPI/type-aliases/AlertParams/index.html +++ b/docs/api/types/FrontendAPI/type-aliases/AlertParams/index.html @@ -5,7 +5,7 @@ AlertParams | AdminForth - + diff --git a/docs/api/types/FrontendAPI/type-aliases/ConfirmParams/index.html b/docs/api/types/FrontendAPI/type-aliases/ConfirmParams/index.html index 9b9dba3fc..61514bbdc 100644 --- a/docs/api/types/FrontendAPI/type-aliases/ConfirmParams/index.html +++ b/docs/api/types/FrontendAPI/type-aliases/ConfirmParams/index.html @@ -5,7 +5,7 @@ ConfirmParams | AdminForth - + diff --git a/docs/api/types/FrontendAPI/type-aliases/FilterParams/index.html b/docs/api/types/FrontendAPI/type-aliases/FilterParams/index.html index 415eb9a6e..ddbb9f8c3 100644 --- a/docs/api/types/FrontendAPI/type-aliases/FilterParams/index.html +++ b/docs/api/types/FrontendAPI/type-aliases/FilterParams/index.html @@ -5,7 +5,7 @@ FilterParams | AdminForth - + diff --git a/docs/api/types/FrontendAPI/type-aliases/Operator/index.html b/docs/api/types/FrontendAPI/type-aliases/Operator/index.html index b4094c1e5..384d97dd4 100644 --- a/docs/api/types/FrontendAPI/type-aliases/Operator/index.html +++ b/docs/api/types/FrontendAPI/type-aliases/Operator/index.html @@ -5,7 +5,7 @@ Operator | AdminForth - + diff --git a/docs/customization/index.html b/docs/customization/index.html index 19b5ce7af..de9d5c725 100644 --- a/docs/customization/index.html +++ b/docs/customization/index.html @@ -5,7 +5,7 @@ Customization | AdminForth - + @@ -49,6 +49,6 @@

Customizing the access control based on resource values

More advanced case, allow to edit apartments only if user is owner of the apartment (defined as user_id), otherwise return error "You are not assigned to this apartment and can't edit it":

-
import type { AdminUser, ActionCheckSource } from  'adminforth/types/AdminForthConfig.js';

async function canModifyAppart({ adminUser, source, meta }: { adminUser: AdminUser, meta: any, source: ActionCheckSource }): boolean {
if (source === ActionCheckSource.DisplayButtons) {
// if check is done for displaying button - we show button to everyone
return true;
}
if (adminUser.isRoot) {
return "Root user can't create appartment, relogin as DB user";
}
const { oldRecord, newRecord } = meta;
if (oldRecord.user_id !== adminUser.dbUser.id) {
return "You are not assigned to this apartment and can't edit it";
}
if (newRecord.user_id !== oldRecord.user_id) {
return "You can't change the owner of the apartment";
}
return true;
}


{
...
resourceId: 'apparts',
...
options: {
allowedActions: {
edit: canModifyAppart,
}
...
}
}
+
import type { AdminUser, ActionCheckSource } from  'adminforth/types/AdminForthConfig.js';

async function canModifyAppart({ adminUser, source, meta }: { adminUser: AdminUser, meta: any, source: ActionCheckSource }): boolean {
if (source === ActionCheckSource.DisplayButtons) {
// if check is done for displaying button - we show button to everyone
return true;
}
if (adminUser.isRoot) {
return false; //root user is not in db so can't be assigned
}
const { oldRecord, newRecord } = meta;
if (oldRecord.user_id !== adminUser.dbUser.id) {
return "You are not assigned to this apartment and can't edit it";
}
if (newRecord.user_id !== oldRecord.user_id) {
return "You can't change the owner of the apartment";
}
return true;
}


{
...
resourceId: 'apparts',
...
options: {
allowedActions: {
edit: canModifyAppart,
}
...
}
}
\ No newline at end of file diff --git a/docs/gettingStarted/index.html b/docs/gettingStarted/index.html index 321ba4677..4302f078c 100644 --- a/docs/gettingStarted/index.html +++ b/docs/gettingStarted/index.html @@ -5,7 +5,7 @@ Getting Started | AdminForth - + diff --git a/docs/glossary/index.html b/docs/glossary/index.html index 6e7ca675b..fa7cb8e74 100644 --- a/docs/glossary/index.html +++ b/docs/glossary/index.html @@ -5,7 +5,7 @@ Glossary | AdminForth - + diff --git a/index.html b/index.html index 83b1c5719..46de4176f 100644 --- a/index.html +++ b/index.html @@ -5,7 +5,7 @@ AdminForth | AdminForth - + diff --git a/markdown-page/index.html b/markdown-page/index.html index ae5e639e3..039f70b9b 100644 --- a/markdown-page/index.html +++ b/markdown-page/index.html @@ -5,7 +5,7 @@ Markdown page example | AdminForth - +