From d7aa9a22ecb991b81f559de5c5761cb0c12a1571 Mon Sep 17 00:00:00 2001 From: Ivan Borshchov Date: Wed, 27 Nov 2024 14:25:04 +0200 Subject: [PATCH] Deploy website - based on 5d18eb0021228eea88057ec073bd9d728e671f21 --- 404.html | 2 +- .../{1590cc1f.b04895f0.js => 1590cc1f.ad26c463.js} | 2 +- .../{186edb45.46c8761b.js => 186edb45.5998b372.js} | 2 +- assets/js/29a082d0.20fd1cee.js | 1 + assets/js/29a082d0.a54547a4.js | 1 - assets/js/b3491139.5bb81a70.js | 1 - assets/js/b3491139.783f762c.js | 1 + .../{ed5bf3ae.d137c237.js => ed5bf3ae.a4b80cf8.js} | 2 +- ...me~main.305055de.js => runtime~main.c774d9ca.js} | 2 +- blog/ai-blog/index.html | 2 +- blog/archive/index.html | 2 +- blog/chatgpt-plugin/index.html | 2 +- .../index.html | 2 +- blog/compose-ec2-deployment/index.html | 2 +- blog/index.html | 2 +- blog/tags/aws/index.html | 2 +- blog/tags/chatgpt/index.html | 2 +- blog/tags/github-actions/index.html | 2 +- blog/tags/index.html | 2 +- blog/tags/nuxt/index.html | 2 +- blog/tags/plugin/index.html | 2 +- blog/tags/terraform/index.html | 2 +- docs/api/index.html | 2 +- docs/api/types/Back/classes/Filters/index.html | 2 +- docs/api/types/Back/classes/Sorts/index.html | 2 +- docs/api/types/Back/index.html | 2 +- .../Back/interfaces/AdminForthBulkAction/index.html | 2 +- .../Back/interfaces/AdminForthConfig/index.html | 2 +- .../AdminForthConfigCustomization/index.html | 2 +- .../interfaces/AdminForthForeignResource/index.html | 2 +- .../interfaces/AdminForthInputConfig/index.html | 2 +- .../Back/interfaces/AdminForthResource/index.html | 2 +- .../interfaces/AdminForthResourceColumn/index.html | 2 +- .../interfaces/AdminForthResourceInput/index.html | 2 +- .../types/Back/interfaces/IAdminForth/index.html | 2 +- .../Back/interfaces/IAdminForthAuth/index.html | 2 +- .../IAdminForthDataSourceConnector/index.html | 2 +- .../IAdminForthDataSourceConnectorBase/index.html | 2 +- .../index.html | 2 +- .../Back/interfaces/IAdminForthFilter/index.html | 2 +- .../interfaces/IAdminForthHttpResponse/index.html | 2 +- .../Back/interfaces/IAdminForthPlugin/index.html | 2 +- .../Back/interfaces/IAdminForthRestAPI/index.html | 2 +- .../Back/interfaces/IAdminForthSort/index.html | 2 +- .../types/Back/interfaces/ICodeInjector/index.html | 2 +- .../Back/interfaces/IConfigValidator/index.html | 2 +- .../Back/interfaces/IExpressHttpServer/index.html | 2 +- .../types/Back/interfaces/IHttpServer/index.html | 2 +- .../Back/interfaces/IOperationalResource/index.html | 2 +- .../Back/interfaces/IWebSocketBroker/index.html | 2 +- .../Back/interfaces/IWebSocketClient/index.html | 2 +- .../Back/interfaces/ResourceOptions/index.html | 2 +- .../Back/interfaces/ResourceOptionsInput/index.html | 2 +- .../type-aliases/AdminForthDataSource/index.html | 2 +- .../AfterDataSourceResponseFunction/index.html | 2 +- .../Back/type-aliases/AfterSaveFunction/index.html | 2 +- .../Back/type-aliases/AllowedActionValue/index.html | 2 +- .../Back/type-aliases/AllowedActions/index.html | 2 +- .../type-aliases/AllowedActionsInput/index.html | 2 +- .../BeforeDataSourceRequestFunction/index.html | 2 +- .../BeforeLoginConfirmationFunction/index.html | 2 +- .../Back/type-aliases/BeforeSaveFunction/index.html | 2 +- .../types/Back/type-aliases/FDataFilter/index.html | 2 +- .../types/Back/type-aliases/FDataSort/index.html | 2 +- .../enumerations/ActionCheckSource/index.html | 4 ++-- .../enumerations/AdminForthDataTypes/index.html | 2 +- .../AdminForthFilterOperators/index.html | 2 +- .../enumerations/AdminForthMenuTypes/index.html | 2 +- .../enumerations/AdminForthResourcePages/index.html | 2 +- .../AdminForthSortDirections/index.html | 2 +- .../enumerations/AllowedActionsEnum/index.html | 2 +- docs/api/types/Common/index.html | 2 +- .../AdminForthBulkActionCommon/index.html | 2 +- .../interfaces/AdminForthColumnEnumItem/index.html | 2 +- .../AdminForthComponentDeclarationFull/index.html | 2 +- .../AdminForthConfigForFrontend/index.html | 2 +- .../interfaces/AdminForthConfigMenuItem/index.html | 2 +- .../interfaces/AdminForthFieldComponents/index.html | 2 +- .../AdminForthForeignResourceCommon/index.html | 2 +- .../AdminForthResourceColumnCommon/index.html | 2 +- .../AdminForthResourceColumnInputCommon/index.html | 2 +- .../interfaces/AdminForthResourceCommon/index.html | 2 +- .../AdminForthResourceInputCommon/index.html | 2 +- .../types/Common/interfaces/AdminUser/index.html | 2 +- .../interfaces/GetBaseConfigResponse/index.html | 2 +- .../Common/interfaces/ResourceVeryShort/index.html | 2 +- .../api/types/Common/interfaces/UserData/index.html | 2 +- .../AdminForthComponentDeclaration/index.html | 2 +- .../type-aliases/AllowedActionsResolved/index.html | 2 +- .../AnnouncementBadgeResponse/index.html | 2 +- .../Common/type-aliases/ValidationObject/index.html | 2 +- .../enumerations/AlertVariant/index.html | 2 +- docs/api/types/FrontendAPI/index.html | 2 +- .../interfaces/FrontendAPIInterface/index.html | 2 +- .../FrontendAPI/type-aliases/AlertParams/index.html | 2 +- .../type-aliases/ConfirmParams/index.html | 2 +- .../tutorial/Advanced/plugin-development/index.html | 2 +- docs/tutorial/Customization/afcl/index.html | 13 ++++++++----- docs/tutorial/Customization/alert/index.html | 2 +- docs/tutorial/Customization/branding/index.html | 2 +- docs/tutorial/Customization/bulkActions/index.html | 2 +- .../Customization/customFieldRendering/index.html | 2 +- docs/tutorial/Customization/customPages/index.html | 2 +- docs/tutorial/Customization/dataApi/index.html | 2 +- docs/tutorial/Customization/hooks/index.html | 2 +- .../Customization/limitingAccess/index.html | 6 +++--- .../Customization/menuConfiguration/index.html | 2 +- .../Customization/pageInjections/index.html | 2 +- docs/tutorial/Customization/security/index.html | 2 +- .../Customization/standardPagesTuning/index.html | 7 +++++-- .../Customization/virtualColumns/index.html | 2 +- docs/tutorial/Customization/websocket/index.html | 2 +- docs/tutorial/Plugins/AuditLog/index.html | 2 +- docs/tutorial/Plugins/ForeignInlineList/index.html | 2 +- docs/tutorial/Plugins/RichEditor/index.html | 2 +- docs/tutorial/Plugins/TwoFactorsAuth/index.html | 2 +- docs/tutorial/Plugins/chat-gpt/index.html | 2 +- .../Plugins/email-password-reset/index.html | 2 +- docs/tutorial/Plugins/import-export/index.html | 2 +- docs/tutorial/Plugins/open-signup/index.html | 2 +- docs/tutorial/Plugins/upload/index.html | 2 +- docs/tutorial/deploy/index.html | 2 +- docs/tutorial/gettingStarted/index.html | 6 +++--- docs/tutorial/glossary/index.html | 2 +- docs/tutorial/helloWorld/index.html | 2 +- index.html | 2 +- search/index.html | 2 +- 127 files changed, 141 insertions(+), 135 deletions(-) rename assets/js/{1590cc1f.b04895f0.js => 1590cc1f.ad26c463.js} (60%) rename assets/js/{186edb45.46c8761b.js => 186edb45.5998b372.js} (55%) create mode 100644 assets/js/29a082d0.20fd1cee.js delete mode 100644 assets/js/29a082d0.a54547a4.js delete mode 100644 assets/js/b3491139.5bb81a70.js create mode 100644 assets/js/b3491139.783f762c.js rename assets/js/{ed5bf3ae.d137c237.js => ed5bf3ae.a4b80cf8.js} (88%) rename assets/js/{runtime~main.305055de.js => runtime~main.c774d9ca.js} (96%) diff --git a/404.html b/404.html index 11f7772ad..817353fe5 100644 --- a/404.html +++ b/404.html @@ -15,7 +15,7 @@ - + diff --git a/assets/js/1590cc1f.b04895f0.js b/assets/js/1590cc1f.ad26c463.js similarity index 60% rename from assets/js/1590cc1f.b04895f0.js rename to assets/js/1590cc1f.ad26c463.js index 9a0038d5e..72d1f5cd9 100644 --- a/assets/js/1590cc1f.b04895f0.js +++ b/assets/js/1590cc1f.ad26c463.js @@ -1 +1 @@ -"use strict";(self.webpackChunkadminforth=self.webpackChunkadminforth||[]).push([[2621],{1186:(e,t,n)=>{n.r(t),n.d(t,{assets:()=>o,contentTitle:()=>l,default:()=>x,frontMatter:()=>r,metadata:()=>c,toc:()=>d});var s=n(4848),i=n(8453);const r={},l="ActionCheckSource",c={id:"api/types/Common/enumerations/ActionCheckSource",title:"ActionCheckSource",description:"Enumeration Members",source:"@site/docs/api/types/Common/enumerations/ActionCheckSource.md",sourceDirName:"api/types/Common/enumerations",slug:"/api/types/Common/enumerations/ActionCheckSource",permalink:"/docs/api/types/Common/enumerations/ActionCheckSource",draft:!1,unlisted:!1,tags:[],version:"current",frontMatter:{},sidebar:"apiSidebar",previous:{title:"types/Common",permalink:"/docs/api/types/Common/"},next:{title:"AdminForthDataTypes",permalink:"/docs/api/types/Common/enumerations/AdminForthDataTypes"}},o={},d=[{value:"Enumeration Members",id:"enumeration-members",level:2}];function h(e){const t={code:"code",h1:"h1",h2:"h2",table:"table",tbody:"tbody",td:"td",th:"th",thead:"thead",tr:"tr",...(0,i.R)(),...e.components};return(0,s.jsxs)(s.Fragment,{children:[(0,s.jsx)(t.h1,{id:"actionchecksource",children:"ActionCheckSource"}),"\n",(0,s.jsx)(t.h2,{id:"enumeration-members",children:"Enumeration Members"}),"\n",(0,s.jsxs)(t.table,{children:[(0,s.jsx)(t.thead,{children:(0,s.jsxs)(t.tr,{children:[(0,s.jsx)(t.th,{style:{textAlign:"left"},children:"Enumeration Member"}),(0,s.jsx)(t.th,{style:{textAlign:"left"},children:"Value"})]})}),(0,s.jsxs)(t.tbody,{children:[(0,s.jsxs)(t.tr,{children:[(0,s.jsx)(t.td,{style:{textAlign:"left"},children:(0,s.jsx)(t.code,{children:"BulkActionRequest"})}),(0,s.jsx)(t.td,{style:{textAlign:"left"},children:(0,s.jsx)(t.code,{children:'"bulkActionRequest"'})})]}),(0,s.jsxs)(t.tr,{children:[(0,s.jsx)(t.td,{style:{textAlign:"left"},children:(0,s.jsx)(t.code,{children:"CreateRequest"})}),(0,s.jsx)(t.td,{style:{textAlign:"left"},children:(0,s.jsx)(t.code,{children:'"createRequest"'})})]}),(0,s.jsxs)(t.tr,{children:[(0,s.jsx)(t.td,{style:{textAlign:"left"},children:(0,s.jsx)(t.code,{children:"DeleteRequest"})}),(0,s.jsx)(t.td,{style:{textAlign:"left"},children:(0,s.jsx)(t.code,{children:'"deleteRequest"'})})]}),(0,s.jsxs)(t.tr,{children:[(0,s.jsx)(t.td,{style:{textAlign:"left"},children:(0,s.jsx)(t.code,{children:"DisplayButtons"})}),(0,s.jsx)(t.td,{style:{textAlign:"left"},children:(0,s.jsx)(t.code,{children:'"displayButtons"'})})]}),(0,s.jsxs)(t.tr,{children:[(0,s.jsx)(t.td,{style:{textAlign:"left"},children:(0,s.jsx)(t.code,{children:"EditRequest"})}),(0,s.jsx)(t.td,{style:{textAlign:"left"},children:(0,s.jsx)(t.code,{children:'"editRequest"'})})]}),(0,s.jsxs)(t.tr,{children:[(0,s.jsx)(t.td,{style:{textAlign:"left"},children:(0,s.jsx)(t.code,{children:"ListRequest"})}),(0,s.jsx)(t.td,{style:{textAlign:"left"},children:(0,s.jsx)(t.code,{children:'"listRequest"'})})]}),(0,s.jsxs)(t.tr,{children:[(0,s.jsx)(t.td,{style:{textAlign:"left"},children:(0,s.jsx)(t.code,{children:"ShowRequest"})}),(0,s.jsx)(t.td,{style:{textAlign:"left"},children:(0,s.jsx)(t.code,{children:'"showRequest"'})})]})]})]})]})}function x(e={}){const{wrapper:t}={...(0,i.R)(),...e.components};return t?(0,s.jsx)(t,{...e,children:(0,s.jsx)(h,{...e})}):h(e)}},8453:(e,t,n)=>{n.d(t,{R:()=>l,x:()=>c});var s=n(6540);const i={},r=s.createContext(i);function l(e){const t=s.useContext(r);return s.useMemo((function(){return"function"==typeof e?e(t):{...t,...e}}),[t,e])}function c(e){let t;return t=e.disableParentContext?"function"==typeof e.components?e.components(i):e.components||i:l(e.components),s.createElement(r.Provider,{value:t},e.children)}}}]); \ No newline at end of file +"use strict";(self.webpackChunkadminforth=self.webpackChunkadminforth||[]).push([[2621],{1186:(e,t,n)=>{n.r(t),n.d(t,{assets:()=>d,contentTitle:()=>r,default:()=>x,frontMatter:()=>l,metadata:()=>c,toc:()=>o});var s=n(4848),i=n(8453);const l={},r="ActionCheckSource",c={id:"api/types/Common/enumerations/ActionCheckSource",title:"ActionCheckSource",description:"Enumeration Members",source:"@site/docs/api/types/Common/enumerations/ActionCheckSource.md",sourceDirName:"api/types/Common/enumerations",slug:"/api/types/Common/enumerations/ActionCheckSource",permalink:"/docs/api/types/Common/enumerations/ActionCheckSource",draft:!1,unlisted:!1,tags:[],version:"current",frontMatter:{},sidebar:"apiSidebar",previous:{title:"types/Common",permalink:"/docs/api/types/Common/"},next:{title:"AdminForthDataTypes",permalink:"/docs/api/types/Common/enumerations/AdminForthDataTypes"}},d={},o=[{value:"Enumeration Members",id:"enumeration-members",level:2}];function h(e){const t={code:"code",h1:"h1",h2:"h2",table:"table",tbody:"tbody",td:"td",th:"th",thead:"thead",tr:"tr",...(0,i.R)(),...e.components};return(0,s.jsxs)(s.Fragment,{children:[(0,s.jsx)(t.h1,{id:"actionchecksource",children:"ActionCheckSource"}),"\n",(0,s.jsx)(t.h2,{id:"enumeration-members",children:"Enumeration Members"}),"\n",(0,s.jsxs)(t.table,{children:[(0,s.jsx)(t.thead,{children:(0,s.jsxs)(t.tr,{children:[(0,s.jsx)(t.th,{style:{textAlign:"left"},children:"Enumeration Member"}),(0,s.jsx)(t.th,{style:{textAlign:"left"},children:"Value"})]})}),(0,s.jsxs)(t.tbody,{children:[(0,s.jsxs)(t.tr,{children:[(0,s.jsx)(t.td,{style:{textAlign:"left"},children:(0,s.jsx)(t.code,{children:"BulkActionRequest"})}),(0,s.jsx)(t.td,{style:{textAlign:"left"},children:(0,s.jsx)(t.code,{children:'"bulkActionRequest"'})})]}),(0,s.jsxs)(t.tr,{children:[(0,s.jsx)(t.td,{style:{textAlign:"left"},children:(0,s.jsx)(t.code,{children:"CreateRequest"})}),(0,s.jsx)(t.td,{style:{textAlign:"left"},children:(0,s.jsx)(t.code,{children:'"createRequest"'})})]}),(0,s.jsxs)(t.tr,{children:[(0,s.jsx)(t.td,{style:{textAlign:"left"},children:(0,s.jsx)(t.code,{children:"DeleteRequest"})}),(0,s.jsx)(t.td,{style:{textAlign:"left"},children:(0,s.jsx)(t.code,{children:'"deleteRequest"'})})]}),(0,s.jsxs)(t.tr,{children:[(0,s.jsx)(t.td,{style:{textAlign:"left"},children:(0,s.jsx)(t.code,{children:"DisplayButtons"})}),(0,s.jsx)(t.td,{style:{textAlign:"left"},children:(0,s.jsx)(t.code,{children:'"displayButtons"'})})]}),(0,s.jsxs)(t.tr,{children:[(0,s.jsx)(t.td,{style:{textAlign:"left"},children:(0,s.jsx)(t.code,{children:"EditLoadRequest"})}),(0,s.jsx)(t.td,{style:{textAlign:"left"},children:(0,s.jsx)(t.code,{children:'"editLoadRequest"'})})]}),(0,s.jsxs)(t.tr,{children:[(0,s.jsx)(t.td,{style:{textAlign:"left"},children:(0,s.jsx)(t.code,{children:"EditRequest"})}),(0,s.jsx)(t.td,{style:{textAlign:"left"},children:(0,s.jsx)(t.code,{children:'"editRequest"'})})]}),(0,s.jsxs)(t.tr,{children:[(0,s.jsx)(t.td,{style:{textAlign:"left"},children:(0,s.jsx)(t.code,{children:"ListRequest"})}),(0,s.jsx)(t.td,{style:{textAlign:"left"},children:(0,s.jsx)(t.code,{children:'"listRequest"'})})]}),(0,s.jsxs)(t.tr,{children:[(0,s.jsx)(t.td,{style:{textAlign:"left"},children:(0,s.jsx)(t.code,{children:"ShowRequest"})}),(0,s.jsx)(t.td,{style:{textAlign:"left"},children:(0,s.jsx)(t.code,{children:'"showRequest"'})})]})]})]})]})}function x(e={}){const{wrapper:t}={...(0,i.R)(),...e.components};return t?(0,s.jsx)(t,{...e,children:(0,s.jsx)(h,{...e})}):h(e)}},8453:(e,t,n)=>{n.d(t,{R:()=>r,x:()=>c});var s=n(6540);const i={},l=s.createContext(i);function r(e){const t=s.useContext(l);return s.useMemo((function(){return"function"==typeof e?e(t):{...t,...e}}),[t,e])}function c(e){let t;return t=e.disableParentContext?"function"==typeof e.components?e.components(i):e.components||i:r(e.components),s.createElement(l.Provider,{value:t},e.children)}}}]); \ No newline at end of file diff --git a/assets/js/186edb45.46c8761b.js b/assets/js/186edb45.5998b372.js similarity index 55% rename from assets/js/186edb45.46c8761b.js rename to assets/js/186edb45.5998b372.js index be58335e3..2a8b330c0 100644 --- a/assets/js/186edb45.46c8761b.js +++ b/assets/js/186edb45.5998b372.js @@ -1 +1 @@ -"use strict";(self.webpackChunkadminforth=self.webpackChunkadminforth||[]).push([[1980],{6777:(e,n,t)=>{t.r(n),t.d(n,{assets:()=>d,contentTitle:()=>s,default:()=>u,frontMatter:()=>i,metadata:()=>o,toc:()=>l});var r=t(4848),a=t(8453);const i={},s="Getting Started",o={id:"tutorial/gettingStarted",title:"Getting Started",description:"This Getting Started Page has some explanations and tables with various field types.",source:"@site/docs/tutorial/001-gettingStarted.md",sourceDirName:"tutorial",slug:"/tutorial/gettingStarted",permalink:"/docs/tutorial/gettingStarted",draft:!1,unlisted:!1,tags:[],version:"current",sidebarPosition:1,frontMatter:{},sidebar:"tutorialSidebar",next:{title:"Hello world app",permalink:"/docs/tutorial/helloWorld"}},d={},l=[{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:"Database creation",id:"database-creation",level:2},{value:"Generating fake records",id:"generating-fake-records",level:2},{value:"Possible configuration options",id:"possible-configuration-options",level:2}];function c(e){const n={a:"a",blockquote:"blockquote",code:"code",h1:"h1",h2:"h2",img:"img",li:"li",ol:"ol",p:"p",pre:"pre",ul:"ul",...(0,a.R)(),...e.components};return(0,r.jsxs)(r.Fragment,{children:[(0,r.jsx)(n.h1,{id:"getting-started",children:"Getting Started"}),"\n",(0,r.jsxs)(n.p,{children:["This Getting Started Page has some explanations and tables with various field types.\nFor faster and shorter hello world example check out ",(0,r.jsx)(n.a,{href:"/docs/tutorial/helloWorld",children:"Hello World"})]}),"\n",(0,r.jsx)(n.h2,{id:"prerequisites",children:"Prerequisites"}),"\n",(0,r.jsx)(n.p,{children:"AdminForth requires Node v18 or higher:"}),"\n",(0,r.jsx)(n.pre,{children:(0,r.jsx)(n.code,{className:"language-bash",children:"nvm install 20\nnvm alias default 20\nnvm use 20\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 init -y\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.pre,{children:(0,r.jsx)(n.code,{className:"language-bash",children:"npm install express\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 @types/express @types/node --save-dev\necho \'{\n "compilerOptions": {\n "target": "esnext",\n "module": "nodenext",\n "esModuleInterop": true,\n "forceConsistentCasingInFileNames": true,\n "strict": true\n },\n "exclude": ["node_modules", "dist"]\n}\' > tsconfig.json\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 back-office 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 back-office. For most DBs 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:'}),"\n",(0,r.jsxs)(n.ul,{children:["\n",(0,r.jsx)(n.li,{children:"how each field will be rendered"}),"\n",(0,r.jsx)(n.li,{children:"create own pages e.g. Dashboards"}),"\n",(0,r.jsx)(n.li,{children:"insert injections into standard pages (e.g. add diagram to list view)"}),"\n"]}),"\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:["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.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",metastring:'title="./package.json"',children:'{\n ...\n//diff-add\n "type": "module",\n "scripts": {\n ...\n//diff-add\n "start": "tsx watch --env-file=.env index.ts"\n },\n}\n'})}),"\n",(0,r.jsxs)(n.p,{children:["Create ",(0,r.jsx)(n.code,{children:".env"})," file in root directory with following content:"]}),"\n",(0,r.jsx)(n.pre,{children:(0,r.jsx)(n.code,{className:"language-bash",metastring:'title="./.env"',children:"DATABASE_FILE=./db.sqlite\nDATABASE_FILE_URL=file:${DATABASE_FILE}\nADMINFORTH_SECRET=123\nNODE_ENV=development\n"})}),"\n",(0,r.jsxs)(n.blockquote,{children:["\n",(0,r.jsx)(n.p,{children:"\u261d\ufe0f In production:"}),"\n",(0,r.jsxs)(n.ol,{children:["\n",(0,r.jsxs)(n.li,{children:["you should set ",(0,r.jsx)(n.code,{children:"NODE_ENV"})," to ",(0,r.jsx)(n.code,{children:"production"})," so it will not waste extra resources on hot reload."]}),"\n",(0,r.jsxs)(n.li,{children:["You should autogenerate ",(0,r.jsx)(n.code,{children:"ADMINFORTH_SECRET"})]}),"\n"]}),"\n"]}),"\n",(0,r.jsxs)(n.blockquote,{children:["\n",(0,r.jsxs)(n.p,{children:["\u261d\ufe0f If you are using Git, obviously you should make sure you will never commit ",(0,r.jsx)(n.code,{children:".env"})," file to the repository, because\nit might contain your own sensitive secrets. So to follow best practices, we recommend to add ",(0,r.jsx)(n.code,{children:".env"})," into ",(0,r.jsx)(n.code,{children:".gitignore"})," and create ",(0,r.jsx)(n.code,{children:".env.sample"})," as template for other repository users.\nDuring deployment you should set ",(0,r.jsx)(n.code,{children:"ADMINFORTH_SECRET"})," in environment variables of Docker image or in other way without using ",(0,r.jsx)(n.code,{children:".env"})," file."]}),"\n"]}),"\n",(0,r.jsx)(n.h2,{id:"database-creation",children:"Database creation"}),"\n",(0,r.jsxs)(n.blockquote,{children:["\n",(0,r.jsx)(n.p,{children:"\u261d\ufe0f For demo purposes we will create a database using Prisma and SQLite.\nYou can also create it using any other favorite tool or ORM and skip this step."}),"\n"]}),"\n",(0,r.jsxs)(n.p,{children:["Create ",(0,r.jsx)(n.code,{children:"./schema.prisma"})," and put next content there:"]}),"\n",(0,r.jsx)(n.pre,{children:(0,r.jsx)(n.code,{className:"language-text",metastring:'title="./schema.prisma"',children:'generator client {\n provider = "prisma-client-js"\n}\n\ndatasource db {\n provider = "sqlite"\n url = env("DATABASE_FILE_URL")\n}\n\nmodel users {\n id String @id\n created_at DateTime \n email String @unique\n role String \n password_hash String\n}\n\nmodel apartments {\n id String @id\n created_at DateTime? \n title String \n square_meter Float?\n price Decimal\n number_of_rooms Int?\n description String?\n country String?\n listed Boolean\n realtor_id String?\n}\n\n'})}),"\n",(0,r.jsxs)(n.p,{children:["Create database using ",(0,r.jsx)(n.code,{children:"prisma migrate"}),":"]}),"\n",(0,r.jsx)(n.pre,{children:(0,r.jsx)(n.code,{className:"language-bash",children:"npx --yes prisma migrate dev --name init\n"})}),"\n",(0,r.jsxs)(n.blockquote,{children:["\n",(0,r.jsxs)(n.p,{children:["\u261d\ufe0f In future, if you will need to change schema, you can create new migration with ",(0,r.jsx)(n.code,{children:"npx prisma migrate dev --name "})]}),"\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-ts",metastring:'title="./index.ts"',children:"import express from 'express';\nimport AdminForth, { Filters } from 'adminforth';\nimport usersResource from \"./resources/users\";\nimport apartmentsResource from \"./resources/apartments\";\n\n\nconst ADMIN_BASE_URL = '';\n\nexport const admin = new AdminForth({\n baseUrl : ADMIN_BASE_URL,\n auth: {\n usersResourceId: 'users', // resource to get user during login\n usernameField: 'email', // field where username is stored, should exist in resource\n passwordHashField: 'password_hash',\n rememberMeDays: 30, // users who will check \"remember me\" will stay logged in for 30 days\n },\n customization: {\n brandName: 'My Admin',\n datesFormat: 'D MMM YY',\n timeFormat: 'HH:mm:ss',\n emptyFieldPlaceholder: '-',\n },\n dataSources: [\n {\n id: 'maindb',\n url: `sqlite://${process.env.DATABASE_FILE}`\n },\n ],\n resources: [\n apartmentsResource,\n usersResource,\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: 'Apartments',\n icon: 'flowbite:home-solid',\n resourceId: 'aparts',\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\nif (import.meta.url === `file://${process.argv[1]}`) {\n // if script is executed directly e.g. node index.ts or npm start\n\n\n const app = express()\n app.use(express.json());\n const port = 3500;\n\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 // serve after you added all api\n admin.express.serve(app)\n\n admin.discoverDatabases().then(async () => {\n if (!await admin.resource('users').get([Filters.EQ('email', 'adminforth')])) {\n await admin.resource('users').create({\n email: 'adminforth',\n password_hash: await AdminForth.Utils.generatePasswordHash('adminforth'),\n role: 'superadmin',\n });\n }\n });\n\n admin.express.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"})}),"\n",(0,r.jsxs)(n.p,{children:["Next step you need to create ",(0,r.jsx)(n.code,{children:"resources"})," folder."]}),"\n",(0,r.jsxs)(n.p,{children:["Create ",(0,r.jsx)(n.code,{children:"apartments.ts"})," in ",(0,r.jsx)(n.code,{children:"resources"}),":"]}),"\n",(0,r.jsx)(n.pre,{children:(0,r.jsx)(n.code,{className:"language-ts",metastring:'title="/apartments.ts"',children:"import { AdminForthDataTypes, AdminForthResource } from 'adminforth';\n\nexport default {\n dataSource: 'maindb',\n table: 'apartments',\n resourceId: 'aparts', // resourceId is defaulted to table name but you can redefine it like this 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, defaulted to uppercased name\n showIn: ['filter', 'show'], // show column in filter and in show page\n primaryKey: true,\n fillOnCreate: ({ initialRecord, adminUser }) => Math.random().toString(36).substring(7), // called during creation to generate content of field, 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'], // all available options\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: AdminForthDataTypes.DATETIME,\n allowMinMaxQuery: true,\n showIn: ['list', 'filter', 'show', 'edit'],\n fillOnCreate: ({ initialRecord, adminUser }) => (new Date()).toISOString(),\n },\n {\n name: 'price',\n allowMinMaxQuery: true, // use better experience for filtering e.g. date range, set it only if you have index on this column or if you sure there will be low number of rows\n editingNote: 'Price is in USD', // you can put a note near field 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 columns so users will not be able to enter more/less\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 showIn: ['show', 'edit', 'create', 'filter'],\n },\n {\n name: 'country',\n enum: [{\n value: 'US',\n label: 'United States'\n }, {\n value: 'DE',\n label: 'Germany'\n }, {\n value: 'FR',\n label: 'France'\n }, {\n value: 'GB',\n label: 'United Kingdom'\n }, {\n value: 'NL',\n label: 'Netherlands'\n }, {\n value: 'IT',\n label: 'Italy'\n }, {\n value: 'ES',\n label: 'Spain'\n }, {\n value: 'DK',\n label: 'Denmark'\n }, {\n value: 'PL',\n label: 'Poland'\n }, {\n value: 'UA',\n label: 'Ukraine'\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: 'realtor_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} as AdminForthResource;\n"})}),"\n",(0,r.jsxs)(n.p,{children:["Create ",(0,r.jsx)(n.code,{children:"users.ts"})," in ",(0,r.jsx)(n.code,{children:"resources"}),":"]}),"\n",(0,r.jsx)(n.pre,{children:(0,r.jsx)(n.code,{className:"language-ts",metastring:'title="/users.ts"',children:"import AdminForth, { AdminForthDataTypes, AdminForthResource } from 'adminforth';\nexport default {\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 // you can also use AdminForth.Utils.EMAIL_VALIDATOR which is alias to this object \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: AdminForthDataTypes.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 type: AdminForthDataTypes.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 minLength: 8,\n validation: [\n // request to have at least 1 digit, 1 upper case, 1 lower case\n AdminForth.Utils.PASSWORD_VALIDATORS.UP_LOW_NUM,\n ],\n },\n { name: 'password_hash', backendOnly: true, showIn: [] }\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 };\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 }\n },\n },\n }\n} as AdminForthResource;\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(8332).A+"",width:"2428",height:"1932"})}),"\n",(0,r.jsx)(n.h2,{id:"generating-fake-records",children:"Generating fake records"}),"\n",(0,r.jsx)(n.pre,{children:(0,r.jsx)(n.code,{className:"language-ts",metastring:'title="./index.ts"',children:"//diff-add\nasync function seedDatabase() {\n//diff-add\n if (await admin.resource('aparts').count() > 0) {\n//diff-add\n return\n//diff-add \n }\n//diff-add \n for (let i = 0; i <= 50; i++) {\n//diff-add \n await admin.resource('aparts').create({\n//diff-add \n id: `${i}`,\n//diff-add \n title: `Apartment ${i}`,\n//diff-add \n square_meter: (Math.random() * 100).toFixed(1),\n//diff-add \n price: (Math.random() * 10000).toFixed(2),\n//diff-add \n number_of_rooms: Math.floor(Math.random() * 4) + 1,\n//diff-add \n description: 'Next gen apartments',\n//diff-add \n created_at: (new Date(Date.now() - Math.random() * 60 * 60 * 24 * 14 * 1000)).toISOString(),\n//diff-add \n listed: i % 2 == 0,\n//diff-add \n country: `${['US', 'DE', 'FR', 'GB', 'NL', 'IT', 'ES', 'DK', 'PL', 'UA'][Math.floor(Math.random() * 10)]}`\n//diff-add \n });\n//diff-add \n };\n//diff-add \n};\n\nif (import.meta.url === `file://${process.argv[1]}`) {\n\n ...\n\n admin.discoverDatabases().then(async () => {\n if (!await admin.resource('users').get([Filters.EQ('email', 'adminforth')])) {\n await admin.resource('users').create({\n email: 'adminforth',\n password_hash: await AdminForth.Utils.generatePasswordHash('adminforth'),\n role: 'superadmin',\n });\n }\n//diff-add\n await seedDatabase();\n });\n\n"})}),"\n",(0,r.jsxs)(n.p,{children:["This will create records during first launch. Now you should see:\n",(0,r.jsx)(n.img,{alt:"alt text",src:t(4973).A+"",width:"3700",height:"1932"})]}),"\n",(0,r.jsx)(n.h2,{id:"possible-configuration-options",children:"Possible configuration options"}),"\n",(0,r.jsxs)(n.p,{children:["Check ",(0,r.jsx)(n.a,{href:"/docs/api/types/Back/interfaces/AdminForthConfig",children:"AdminForthConfig"})," for all possible options."]})]})}function u(e={}){const{wrapper:n}={...(0,a.R)(),...e.components};return n?(0,r.jsx)(n,{...e,children:(0,r.jsx)(c,{...e})}):c(e)}},8332:(e,n,t)=>{t.d(n,{A:()=>r});const r=t.p+"assets/images/localhost_3500_login-22b59511349c51948267c9a4080e4d87.png"},4973:(e,n,t)=>{t.d(n,{A:()=>r});const r=t.p+"assets/images/localhost_3500_resource_aparts-dddac951816a2a7b58c84b6348828ecb.png"},8453:(e,n,t)=>{t.d(n,{R:()=>s,x:()=>o});var r=t(6540);const a={},i=r.createContext(a);function s(e){const n=r.useContext(i);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(a):e.components||a:s(e.components),r.createElement(i.Provider,{value:n},e.children)}}}]); \ No newline at end of file +"use strict";(self.webpackChunkadminforth=self.webpackChunkadminforth||[]).push([[1980],{6777:(e,n,t)=>{t.r(n),t.d(n,{assets:()=>d,contentTitle:()=>s,default:()=>u,frontMatter:()=>i,metadata:()=>o,toc:()=>l});var r=t(4848),a=t(8453);const i={},s="Getting Started",o={id:"tutorial/gettingStarted",title:"Getting Started",description:"This Getting Started Page has some explanations and tables with various field types.",source:"@site/docs/tutorial/001-gettingStarted.md",sourceDirName:"tutorial",slug:"/tutorial/gettingStarted",permalink:"/docs/tutorial/gettingStarted",draft:!1,unlisted:!1,tags:[],version:"current",sidebarPosition:1,frontMatter:{},sidebar:"tutorialSidebar",next:{title:"Hello world app",permalink:"/docs/tutorial/helloWorld"}},d={},l=[{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:"Database creation",id:"database-creation",level:2},{value:"Generating fake records",id:"generating-fake-records",level:2},{value:"Possible configuration options",id:"possible-configuration-options",level:2}];function c(e){const n={a:"a",blockquote:"blockquote",code:"code",h1:"h1",h2:"h2",img:"img",li:"li",ol:"ol",p:"p",pre:"pre",ul:"ul",...(0,a.R)(),...e.components};return(0,r.jsxs)(r.Fragment,{children:[(0,r.jsx)(n.h1,{id:"getting-started",children:"Getting Started"}),"\n",(0,r.jsxs)(n.p,{children:["This Getting Started Page has some explanations and tables with various field types.\nFor faster and shorter hello world example check out ",(0,r.jsx)(n.a,{href:"/docs/tutorial/helloWorld",children:"Hello World"})]}),"\n",(0,r.jsx)(n.h2,{id:"prerequisites",children:"Prerequisites"}),"\n",(0,r.jsx)(n.p,{children:"AdminForth requires Node v18 or higher:"}),"\n",(0,r.jsx)(n.pre,{children:(0,r.jsx)(n.code,{className:"language-bash",children:"nvm install 20\nnvm alias default 20\nnvm use 20\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 init -y\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.pre,{children:(0,r.jsx)(n.code,{className:"language-bash",children:"npm install express\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 @types/express @types/node --save-dev\necho \'{\n "compilerOptions": {\n "target": "esnext",\n "module": "nodenext",\n "esModuleInterop": true,\n "forceConsistentCasingInFileNames": true,\n "strict": true\n },\n "exclude": ["node_modules", "dist"]\n}\' > tsconfig.json\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 back-office 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 back-office. For most DBs 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:'}),"\n",(0,r.jsxs)(n.ul,{children:["\n",(0,r.jsx)(n.li,{children:"how each field will be rendered"}),"\n",(0,r.jsx)(n.li,{children:"create own pages e.g. Dashboards"}),"\n",(0,r.jsx)(n.li,{children:"insert injections into standard pages (e.g. add diagram to list view)"}),"\n"]}),"\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:["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.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",metastring:'title="./package.json"',children:'{\n ...\n//diff-add\n "type": "module",\n "scripts": {\n ...\n//diff-add\n "start": "tsx watch --env-file=.env index.ts"\n },\n}\n'})}),"\n",(0,r.jsxs)(n.p,{children:["Create ",(0,r.jsx)(n.code,{children:".env"})," file in root directory with following content:"]}),"\n",(0,r.jsx)(n.pre,{children:(0,r.jsx)(n.code,{className:"language-bash",metastring:'title="./.env"',children:"DATABASE_FILE=./db.sqlite\nDATABASE_FILE_URL=file:${DATABASE_FILE}\nADMINFORTH_SECRET=123\nNODE_ENV=development\n"})}),"\n",(0,r.jsxs)(n.blockquote,{children:["\n",(0,r.jsx)(n.p,{children:"\u261d\ufe0f In production:"}),"\n",(0,r.jsxs)(n.ol,{children:["\n",(0,r.jsxs)(n.li,{children:["you should set ",(0,r.jsx)(n.code,{children:"NODE_ENV"})," to ",(0,r.jsx)(n.code,{children:"production"})," so it will not waste extra resources on hot reload."]}),"\n",(0,r.jsxs)(n.li,{children:["You should autogenerate ",(0,r.jsx)(n.code,{children:"ADMINFORTH_SECRET"})]}),"\n"]}),"\n"]}),"\n",(0,r.jsxs)(n.blockquote,{children:["\n",(0,r.jsxs)(n.p,{children:["\u261d\ufe0f If you are using Git, obviously you should make sure you will never commit ",(0,r.jsx)(n.code,{children:".env"})," file to the repository, because\nit might contain your own sensitive secrets. So to follow best practices, we recommend to add ",(0,r.jsx)(n.code,{children:".env"})," into ",(0,r.jsx)(n.code,{children:".gitignore"})," and create ",(0,r.jsx)(n.code,{children:".env.sample"})," as template for other repository users.\nDuring deployment you should set ",(0,r.jsx)(n.code,{children:"ADMINFORTH_SECRET"})," in environment variables of Docker image or in other way without using ",(0,r.jsx)(n.code,{children:".env"})," file."]}),"\n"]}),"\n",(0,r.jsx)(n.h2,{id:"database-creation",children:"Database creation"}),"\n",(0,r.jsxs)(n.blockquote,{children:["\n",(0,r.jsx)(n.p,{children:"\u261d\ufe0f For demo purposes we will create a database using Prisma and SQLite.\nYou can also create it using any other favorite tool or ORM and skip this step."}),"\n"]}),"\n",(0,r.jsxs)(n.p,{children:["Create ",(0,r.jsx)(n.code,{children:"./schema.prisma"})," and put next content there:"]}),"\n",(0,r.jsx)(n.pre,{children:(0,r.jsx)(n.code,{className:"language-text",metastring:'title="./schema.prisma"',children:'generator client {\n provider = "prisma-client-js"\n}\n\ndatasource db {\n provider = "sqlite"\n url = env("DATABASE_FILE_URL")\n}\n\nmodel users {\n id String @id\n created_at DateTime \n email String @unique\n role String \n password_hash String\n}\n\nmodel apartments {\n id String @id\n created_at DateTime? \n title String \n square_meter Float?\n price Decimal\n number_of_rooms Int?\n description String?\n country String?\n listed Boolean\n realtor_id String?\n}\n\n'})}),"\n",(0,r.jsxs)(n.p,{children:["Create database using ",(0,r.jsx)(n.code,{children:"prisma migrate"}),":"]}),"\n",(0,r.jsx)(n.pre,{children:(0,r.jsx)(n.code,{className:"language-bash",children:"npx --yes prisma migrate dev --name init\n"})}),"\n",(0,r.jsxs)(n.blockquote,{children:["\n",(0,r.jsxs)(n.p,{children:["\u261d\ufe0f In future, if you will need to change schema, you can create new migration with ",(0,r.jsx)(n.code,{children:"npx prisma migrate dev --name "})]}),"\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-ts",metastring:'title="./index.ts"',children:"import express from 'express';\nimport AdminForth, { Filters } from 'adminforth';\nimport usersResource from \"./resources/users\";\nimport apartmentsResource from \"./resources/apartments\";\n\n\nconst ADMIN_BASE_URL = '';\n\nexport const admin = new AdminForth({\n baseUrl : ADMIN_BASE_URL,\n auth: {\n usersResourceId: 'users', // resource to get user during login\n usernameField: 'email', // field where username is stored, should exist in resource\n passwordHashField: 'password_hash',\n rememberMeDays: 30, // users who will check \"remember me\" will stay logged in for 30 days\n },\n customization: {\n brandName: 'My Admin',\n datesFormat: 'D MMM YY',\n timeFormat: 'HH:mm:ss',\n emptyFieldPlaceholder: '-',\n },\n dataSources: [\n {\n id: 'maindb',\n url: `sqlite://${process.env.DATABASE_FILE}`\n },\n ],\n resources: [\n apartmentsResource,\n usersResource,\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: 'Apartments',\n icon: 'flowbite:home-solid',\n resourceId: 'aparts',\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\nif (import.meta.url === `file://${process.argv[1]}`) {\n // if script is executed directly e.g. node index.ts or npm start\n\n\n const app = express()\n app.use(express.json());\n const port = 3500;\n\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 // serve after you added all api\n admin.express.serve(app)\n\n admin.discoverDatabases().then(async () => {\n if (!await admin.resource('users').get([Filters.EQ('email', 'adminforth')])) {\n await admin.resource('users').create({\n email: 'adminforth',\n password_hash: await AdminForth.Utils.generatePasswordHash('adminforth'),\n role: 'superadmin',\n });\n }\n });\n\n admin.express.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"})}),"\n",(0,r.jsxs)(n.p,{children:["Next step you need to create ",(0,r.jsx)(n.code,{children:"resources"})," folder."]}),"\n",(0,r.jsxs)(n.p,{children:["Create ",(0,r.jsx)(n.code,{children:"apartments.ts"})," in ",(0,r.jsx)(n.code,{children:"resources"}),":"]}),"\n",(0,r.jsx)(n.pre,{children:(0,r.jsx)(n.code,{className:"language-ts",metastring:'title="/apartments.ts"',children:"import { AdminForthDataTypes, AdminForthResourceInput } from 'adminforth';\n\nexport default {\n dataSource: 'maindb',\n table: 'apartments',\n resourceId: 'aparts', // resourceId is defaulted to table name but you can redefine it like this 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, defaulted to uppercased name\n showIn: ['filter', 'show'], // show column in filter and in show page\n primaryKey: true,\n fillOnCreate: ({ initialRecord, adminUser }) => Math.random().toString(36).substring(7), // called during creation to generate content of field, 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'], // all available options\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: AdminForthDataTypes.DATETIME,\n allowMinMaxQuery: true,\n showIn: ['list', 'filter', 'show', 'edit'],\n fillOnCreate: ({ initialRecord, adminUser }) => (new Date()).toISOString(),\n },\n {\n name: 'price',\n allowMinMaxQuery: true, // use better experience for filtering e.g. date range, set it only if you have index on this column or if you sure there will be low number of rows\n editingNote: 'Price is in USD', // you can put a note near field 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 columns so users will not be able to enter more/less\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 showIn: ['show', 'edit', 'create', 'filter'],\n },\n {\n name: 'country',\n enum: [{\n value: 'US',\n label: 'United States'\n }, {\n value: 'DE',\n label: 'Germany'\n }, {\n value: 'FR',\n label: 'France'\n }, {\n value: 'GB',\n label: 'United Kingdom'\n }, {\n value: 'NL',\n label: 'Netherlands'\n }, {\n value: 'IT',\n label: 'Italy'\n }, {\n value: 'ES',\n label: 'Spain'\n }, {\n value: 'DK',\n label: 'Denmark'\n }, {\n value: 'PL',\n label: 'Poland'\n }, {\n value: 'UA',\n label: 'Ukraine'\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: 'realtor_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} as AdminForthResourceInput;\n"})}),"\n",(0,r.jsxs)(n.p,{children:["Create ",(0,r.jsx)(n.code,{children:"users.ts"})," in ",(0,r.jsx)(n.code,{children:"resources"}),":"]}),"\n",(0,r.jsx)(n.pre,{children:(0,r.jsx)(n.code,{className:"language-ts",metastring:'title="/users.ts"',children:"import AdminForth, { AdminForthDataTypes, AdminForthResourceInput } from 'adminforth';\nexport default {\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 // you can also use AdminForth.Utils.EMAIL_VALIDATOR which is alias to this object \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: AdminForthDataTypes.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 type: AdminForthDataTypes.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 minLength: 8,\n validation: [\n // request to have at least 1 digit, 1 upper case, 1 lower case\n AdminForth.Utils.PASSWORD_VALIDATORS.UP_LOW_NUM,\n ],\n },\n { name: 'password_hash', backendOnly: true, showIn: [] }\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 };\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 }\n },\n },\n }\n} as AdminForthResourceInput;\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(8332).A+"",width:"2428",height:"1932"})}),"\n",(0,r.jsx)(n.h2,{id:"generating-fake-records",children:"Generating fake records"}),"\n",(0,r.jsx)(n.pre,{children:(0,r.jsx)(n.code,{className:"language-ts",metastring:'title="./index.ts"',children:"//diff-add\nasync function seedDatabase() {\n//diff-add\n if (await admin.resource('aparts').count() > 0) {\n//diff-add\n return\n//diff-add \n }\n//diff-add \n for (let i = 0; i <= 50; i++) {\n//diff-add \n await admin.resource('aparts').create({\n//diff-add \n id: `${i}`,\n//diff-add \n title: `Apartment ${i}`,\n//diff-add \n square_meter: (Math.random() * 100).toFixed(1),\n//diff-add \n price: (Math.random() * 10000).toFixed(2),\n//diff-add \n number_of_rooms: Math.floor(Math.random() * 4) + 1,\n//diff-add \n description: 'Next gen apartments',\n//diff-add \n created_at: (new Date(Date.now() - Math.random() * 60 * 60 * 24 * 14 * 1000)).toISOString(),\n//diff-add \n listed: i % 2 == 0,\n//diff-add \n country: `${['US', 'DE', 'FR', 'GB', 'NL', 'IT', 'ES', 'DK', 'PL', 'UA'][Math.floor(Math.random() * 10)]}`\n//diff-add \n });\n//diff-add \n };\n//diff-add \n};\n\nif (import.meta.url === `file://${process.argv[1]}`) {\n\n ...\n\n admin.discoverDatabases().then(async () => {\n if (!await admin.resource('users').get([Filters.EQ('email', 'adminforth')])) {\n await admin.resource('users').create({\n email: 'adminforth',\n password_hash: await AdminForth.Utils.generatePasswordHash('adminforth'),\n role: 'superadmin',\n });\n }\n//diff-add\n await seedDatabase();\n });\n\n"})}),"\n",(0,r.jsxs)(n.p,{children:["This will create records during first launch. Now you should see:\n",(0,r.jsx)(n.img,{alt:"alt text",src:t(4973).A+"",width:"3700",height:"1932"})]}),"\n",(0,r.jsx)(n.h2,{id:"possible-configuration-options",children:"Possible configuration options"}),"\n",(0,r.jsxs)(n.p,{children:["Check ",(0,r.jsx)(n.a,{href:"/docs/api/types/Back/interfaces/AdminForthConfig",children:"AdminForthConfig"})," for all possible options."]})]})}function u(e={}){const{wrapper:n}={...(0,a.R)(),...e.components};return n?(0,r.jsx)(n,{...e,children:(0,r.jsx)(c,{...e})}):c(e)}},8332:(e,n,t)=>{t.d(n,{A:()=>r});const r=t.p+"assets/images/localhost_3500_login-22b59511349c51948267c9a4080e4d87.png"},4973:(e,n,t)=>{t.d(n,{A:()=>r});const r=t.p+"assets/images/localhost_3500_resource_aparts-dddac951816a2a7b58c84b6348828ecb.png"},8453:(e,n,t)=>{t.d(n,{R:()=>s,x:()=>o});var r=t(6540);const a={},i=r.createContext(a);function s(e){const n=r.useContext(i);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(a):e.components||a:s(e.components),r.createElement(i.Provider,{value:n},e.children)}}}]); \ No newline at end of file diff --git a/assets/js/29a082d0.20fd1cee.js b/assets/js/29a082d0.20fd1cee.js new file mode 100644 index 000000000..2d0eab66e --- /dev/null +++ b/assets/js/29a082d0.20fd1cee.js @@ -0,0 +1 @@ +"use strict";(self.webpackChunkadminforth=self.webpackChunkadminforth||[]).push([[4753],{8249:(e,n,t)=>{t.r(n),t.d(n,{assets:()=>d,contentTitle:()=>a,default:()=>u,frontMatter:()=>s,metadata:()=>o,toc:()=>l});var r=t(4848),i=t(8453);const s={},a="Standard pages tuning",o={id:"tutorial/Customization/standardPagesTuning",title:"Standard pages tuning",description:"Fields Grouping",source:"@site/docs/tutorial/03-Customization/13-standardPagesTuning.md",sourceDirName:"tutorial/03-Customization",slug:"/tutorial/Customization/standardPagesTuning",permalink:"/docs/tutorial/Customization/standardPagesTuning",draft:!1,unlisted:!1,tags:[],version:"current",sidebarPosition:13,frontMatter:{},sidebar:"tutorialSidebar",previous:{title:"Security",permalink:"/docs/tutorial/Customization/security"},next:{title:"AdminForth Components Library",permalink:"/docs/tutorial/Customization/afcl"}},d={},l=[{value:"Fields Grouping",id:"fields-grouping",level:2},{value:"List",id:"list",level:2},{value:"Default Sorting",id:"default-sorting",level:3},{value:"Page size",id:"page-size",level:3},{value:"Custom row click action",id:"custom-row-click-action",level:3},{value:"Auto-refresh records",id:"auto-refresh-records",level:3},{value:"Creating",id:"creating",level:2},{value:"Fill with default values",id:"fill-with-default-values",level:3},{value:"Link to create form with preset values",id:"link-to-create-form-with-preset-values",level:3},{value:"Editing",id:"editing",level:2}];function c(e){const n={a:"a",blockquote:"blockquote",br:"br",code:"code",h1:"h1",h2:"h2",h3:"h3",img:"img",p:"p",pre:"pre",...(0,i.R)(),...e.components};return(0,r.jsxs)(r.Fragment,{children:[(0,r.jsx)(n.h1,{id:"standard-pages-tuning",children:"Standard pages tuning"}),"\n",(0,r.jsx)(n.h2,{id:"fields-grouping",children:"Fields Grouping"}),"\n",(0,r.jsx)(n.p,{children:'In some cases, you may want to organize data fields into specific groups for better structure and clarity. For example, you could create a "Main Info" group to include columns like title, description, country, and apartment_image. Another group, "Characteristics," could hold attributes such as price, square_meter, number_of_rooms, property_type, and listed. Any values without a specified group will be categorized under "Other."'}),"\n",(0,r.jsx)(n.pre,{children:(0,r.jsx)(n.code,{className:"language-typescript",metastring:'title="./resources/apartments.ts"',children:" resources: [\n {\n ...\n options: {\n ...\n //diff-add\n createEditGroups: [\n //diff-add\n {\n //diff-add\n groupName: 'Main info',\n //diff-add\n columns: ['id','title', 'description', 'country', 'apartment_image']\n //diff-add\n },\n //diff-add\n {\n //diff-add\n groupName: 'Characteristics',\n //diff-add\n columns: ['price', 'square_meter', 'number_of_rooms', \"property_type\", \"listed\"]\n //diff-add\n }\n //diff-add\n ],\n }\n }\n ]\n"})}),"\n",(0,r.jsxs)(n.p,{children:["Here is how it looks:\n",(0,r.jsx)(n.img,{alt:"alt text",src:t(5276).A+"",width:"2281",height:"1129"})]}),"\n",(0,r.jsx)(n.h2,{id:"list",children:"List"}),"\n",(0,r.jsx)(n.h3,{id:"default-sorting",children:"Default Sorting"}),"\n",(0,r.jsx)(n.pre,{children:(0,r.jsx)(n.code,{className:"language-typescript",metastring:'title="./resources/apartments.ts"',children:"import { AdminForthSortDirections } from 'adminforth';\n\n...\n resources: [\n {\n resourceId: 'aparts',\n options: {\n//diff-add\n defaultSort: {\n//diff-add\n columnName: 'created_at',\n//diff-add\n direction: AdminForthSortDirections.ASC, \n//diff-add\n }\n }\n }\n ]\n"})}),"\n",(0,r.jsx)(n.h3,{id:"page-size",children:"Page size"}),"\n",(0,r.jsxs)(n.p,{children:["use ",(0,r.jsx)(n.code,{children:"options.listPageSize"})," to define how many records will be shown on the page"]}),"\n",(0,r.jsx)(n.pre,{children:(0,r.jsx)(n.code,{className:"language-typescript",metastring:'title="./resources/apartments.ts"',children:" resources: [\n {\n resourceId: 'aparts',\n options: {\n ...\n//diff-add\n listPageSize: 10,\n }\n }\n ]\n"})}),"\n",(0,r.jsx)(n.h3,{id:"custom-row-click-action",children:"Custom row click action"}),"\n",(0,r.jsx)(n.p,{children:"By default, when you click on a record in the list view, the show view will be opened."}),"\n",(0,r.jsxs)(n.p,{children:["You can change this behavior by using ",(0,r.jsx)(n.code,{children:"options.listTableClickUrl"}),"."]}),"\n",(0,r.jsx)(n.p,{children:"To disable any action (don't open show) return null:"}),"\n",(0,r.jsx)(n.pre,{children:(0,r.jsx)(n.code,{className:"language-typescript",metastring:'title="./resources/apartments.ts"',children:" resources: [\n {\n resourceId: 'aparts',\n options: {\n ...\n//diff-add\n listTableClickUrl: async (record, adminUser) => null,\n }\n }\n ]\n"})}),"\n",(0,r.jsx)(n.p,{children:"To open a custom page, return URL to the custom page (can start with https://, or relative adminforth path):"}),"\n",(0,r.jsx)(n.pre,{children:(0,r.jsx)(n.code,{className:"language-typescript",metastring:'title="./resources/apartments.ts"',children:" options: {\n ...\n//diff-add\n listTableClickUrl: async (record, adminUser) => {\n return `https://google.com/search?q=${record.name}`;\n }\n }\n"})}),"\n",(0,r.jsxs)(n.p,{children:["If you wish to open the page in a new tab, add ",(0,r.jsx)(n.code,{children:"target=_blank"})," get param to the returned URL:"]}),"\n",(0,r.jsx)(n.pre,{children:(0,r.jsx)(n.code,{className:"language-typescript",metastring:'title="./resources/apartments.ts"',children:" options: {\n ...\n//diff-add\n listTableClickUrl: async (record, adminUser) => {\n return `https://google.com/search?q=${record.name}&target=_blank`;\n }\n }\n"})}),"\n",(0,r.jsx)(n.h3,{id:"auto-refresh-records",children:"Auto-refresh records"}),"\n",(0,r.jsxs)(n.p,{children:[(0,r.jsx)(n.code,{children:"options.listRowsAutoRefreshSeconds"})," might be used to silently refresh records that are loaded (no new records will be fetched if\nthey appear)"]}),"\n",(0,r.jsx)(n.pre,{children:(0,r.jsx)(n.code,{className:"language-typescript",metastring:'title="./resources/apartments.ts"',children:" resources: [\n {\n resourceId: 'aparts',\n hooks: {\n//diff-add\n list: {\n//diff-add\n afterDatasourceResponse: async ({ response }: { response: any }) => { \n//diff-add\n response.forEach((r: any) => {\n//diff-add\n // substitute random country on any load\n//diff-add\n const countries = [ 'US', 'DE', 'FR', 'GB', 'NL', 'IT', 'ES', 'DK', 'PL', 'UA', \n//diff-add\n 'CA', 'AU', 'BR', 'JP', 'CN', 'IN', 'KR', 'TR', 'MX', 'ID']\n//diff-add\n r.country = countries[Math.floor(Math.random() * countries.length)];\n//diff-add\n })\n//diff-add\n return { ok: true, error: \"\" }\n//diff-add\n }\n//diff-add\n }\n },\n options: {\n ...\n//diff-add\n listRowsAutoRefreshSeconds: 1,\n }\n }\n ]\n"})}),"\n",(0,r.jsx)(n.p,{children:(0,r.jsx)(n.img,{alt:"alt text",src:t(8481).A+"",width:"1999",height:"1499"})}),"\n",(0,r.jsx)(n.h2,{id:"creating",children:"Creating"}),"\n",(0,r.jsx)(n.h3,{id:"fill-with-default-values",children:"Fill with default values"}),"\n",(0,r.jsx)(n.p,{children:"Sometimes you want to generate some field value without asking user to fill it. For example createdAt oftenly store time of creation of the record. You can do this by using fillOnCreate:"}),"\n",(0,r.jsx)(n.pre,{children:(0,r.jsx)(n.code,{className:"language-typescript",metastring:'title="./resources/apartments.ts" ',children:"\nnew AdminForth({\n ...\n resources: [\n {\n name: 'apartments',\n fields: [\n ...\n {\n name: 'created_at',\n type: AdminForthDataTypes.DATETIME,\n//diff-add\n fillOnCreate: ({ initialRecord, adminUser }) => (new Date()).toISOString(),\n },\n ],\n },\n ...\n ],\n"})}),"\n",(0,r.jsx)(n.p,{children:"Also you can assign adminUser ID by adminUser.dbUser.id in same hook:"}),"\n",(0,r.jsx)(n.pre,{children:(0,r.jsx)(n.code,{className:"language-typescript",metastring:'title="./resources/apartments.ts"',children:"new AdminForth({\n ...\n resources: [\n {\n name: 'apartments',\n fields: [\n ...\n {\n name: 'created_by',\n type: AdminForthDataTypes.STRING,\n//diff-add\n fillOnCreate: ({ initialRecord, adminUser }) => adminUser.dbUser.id,\n },\n ],\n },\n ...\n ],\n"})}),"\n",(0,r.jsxs)(n.blockquote,{children:["\n",(0,r.jsxs)(n.p,{children:["Same effect can be achieved by using ",(0,r.jsx)(n.a,{href:"/docs/tutorial/Customization/hooks/#modify-the-data-before-it-is-saved-to-the-database",children:"hooks"}),". But ",(0,r.jsx)(n.code,{children:"fillOnCreate"})," might be shorter and more readable."]}),"\n"]}),"\n",(0,r.jsx)(n.h3,{id:"link-to-create-form-with-preset-values",children:"Link to create form with preset values"}),"\n",(0,r.jsx)(n.p,{children:"Sometimes you might need to create a link that will open the create form with some fields pre-filled. For example, you might want to create a link that will open the create form with the realtor_id field pre-filled with the current user's ID."}),"\n",(0,r.jsx)(n.pre,{children:(0,r.jsx)(n.code,{className:"language-html",metastring:'title="./resources/Dashboard.vue',children:"\n\n - + diff --git a/blog/archive/index.html b/blog/archive/index.html index cde6117d8..4a8fa1e31 100644 --- a/blog/archive/index.html +++ b/blog/archive/index.html @@ -15,7 +15,7 @@ - + diff --git a/blog/chatgpt-plugin/index.html b/blog/chatgpt-plugin/index.html index f6e1f28b1..931450863 100644 --- a/blog/chatgpt-plugin/index.html +++ b/blog/chatgpt-plugin/index.html @@ -15,7 +15,7 @@ - + diff --git a/blog/compose-ec2-deployment-github-actions/index.html b/blog/compose-ec2-deployment-github-actions/index.html index 50976a21d..421ecc996 100644 --- a/blog/compose-ec2-deployment-github-actions/index.html +++ b/blog/compose-ec2-deployment-github-actions/index.html @@ -15,7 +15,7 @@ - + diff --git a/blog/compose-ec2-deployment/index.html b/blog/compose-ec2-deployment/index.html index 0c7cbad81..46276759a 100644 --- a/blog/compose-ec2-deployment/index.html +++ b/blog/compose-ec2-deployment/index.html @@ -15,7 +15,7 @@ - + diff --git a/blog/index.html b/blog/index.html index 09815b207..52af9eceb 100644 --- a/blog/index.html +++ b/blog/index.html @@ -15,7 +15,7 @@ - + diff --git a/blog/tags/aws/index.html b/blog/tags/aws/index.html index 33f957baa..de29f1eea 100644 --- a/blog/tags/aws/index.html +++ b/blog/tags/aws/index.html @@ -15,7 +15,7 @@ - + diff --git a/blog/tags/chatgpt/index.html b/blog/tags/chatgpt/index.html index 13736f20c..c874f6fff 100644 --- a/blog/tags/chatgpt/index.html +++ b/blog/tags/chatgpt/index.html @@ -15,7 +15,7 @@ - + diff --git a/blog/tags/github-actions/index.html b/blog/tags/github-actions/index.html index b40d1d2a3..ee59c0236 100644 --- a/blog/tags/github-actions/index.html +++ b/blog/tags/github-actions/index.html @@ -15,7 +15,7 @@ - + diff --git a/blog/tags/index.html b/blog/tags/index.html index 2b96592c7..90bee87be 100644 --- a/blog/tags/index.html +++ b/blog/tags/index.html @@ -15,7 +15,7 @@ - + diff --git a/blog/tags/nuxt/index.html b/blog/tags/nuxt/index.html index 903090fc9..8208cb473 100644 --- a/blog/tags/nuxt/index.html +++ b/blog/tags/nuxt/index.html @@ -15,7 +15,7 @@ - + diff --git a/blog/tags/plugin/index.html b/blog/tags/plugin/index.html index 5fa62be20..6d48be185 100644 --- a/blog/tags/plugin/index.html +++ b/blog/tags/plugin/index.html @@ -15,7 +15,7 @@ - + diff --git a/blog/tags/terraform/index.html b/blog/tags/terraform/index.html index b01c3e64a..2840304b1 100644 --- a/blog/tags/terraform/index.html +++ b/blog/tags/terraform/index.html @@ -15,7 +15,7 @@ - + diff --git a/docs/api/index.html b/docs/api/index.html index eee076c49..c20c02b9e 100644 --- a/docs/api/index.html +++ b/docs/api/index.html @@ -15,7 +15,7 @@ - + diff --git a/docs/api/types/Back/classes/Filters/index.html b/docs/api/types/Back/classes/Filters/index.html index b55cf9a24..9829750b1 100644 --- a/docs/api/types/Back/classes/Filters/index.html +++ b/docs/api/types/Back/classes/Filters/index.html @@ -15,7 +15,7 @@ - + diff --git a/docs/api/types/Back/classes/Sorts/index.html b/docs/api/types/Back/classes/Sorts/index.html index d540882b8..30dccaf3a 100644 --- a/docs/api/types/Back/classes/Sorts/index.html +++ b/docs/api/types/Back/classes/Sorts/index.html @@ -15,7 +15,7 @@ - + diff --git a/docs/api/types/Back/index.html b/docs/api/types/Back/index.html index 703b9d697..03f4e2d6d 100644 --- a/docs/api/types/Back/index.html +++ b/docs/api/types/Back/index.html @@ -15,7 +15,7 @@ - + diff --git a/docs/api/types/Back/interfaces/AdminForthBulkAction/index.html b/docs/api/types/Back/interfaces/AdminForthBulkAction/index.html index 2ba38890b..668a10689 100644 --- a/docs/api/types/Back/interfaces/AdminForthBulkAction/index.html +++ b/docs/api/types/Back/interfaces/AdminForthBulkAction/index.html @@ -15,7 +15,7 @@ - + diff --git a/docs/api/types/Back/interfaces/AdminForthConfig/index.html b/docs/api/types/Back/interfaces/AdminForthConfig/index.html index 2ed713214..9be707721 100644 --- a/docs/api/types/Back/interfaces/AdminForthConfig/index.html +++ b/docs/api/types/Back/interfaces/AdminForthConfig/index.html @@ -15,7 +15,7 @@ - + diff --git a/docs/api/types/Back/interfaces/AdminForthConfigCustomization/index.html b/docs/api/types/Back/interfaces/AdminForthConfigCustomization/index.html index 1d05a9869..d16850e51 100644 --- a/docs/api/types/Back/interfaces/AdminForthConfigCustomization/index.html +++ b/docs/api/types/Back/interfaces/AdminForthConfigCustomization/index.html @@ -15,7 +15,7 @@ - + diff --git a/docs/api/types/Back/interfaces/AdminForthForeignResource/index.html b/docs/api/types/Back/interfaces/AdminForthForeignResource/index.html index fab2bb500..fd325eb32 100644 --- a/docs/api/types/Back/interfaces/AdminForthForeignResource/index.html +++ b/docs/api/types/Back/interfaces/AdminForthForeignResource/index.html @@ -15,7 +15,7 @@ - + diff --git a/docs/api/types/Back/interfaces/AdminForthInputConfig/index.html b/docs/api/types/Back/interfaces/AdminForthInputConfig/index.html index 535b6fac5..8007b2ef4 100644 --- a/docs/api/types/Back/interfaces/AdminForthInputConfig/index.html +++ b/docs/api/types/Back/interfaces/AdminForthInputConfig/index.html @@ -15,7 +15,7 @@ - + diff --git a/docs/api/types/Back/interfaces/AdminForthResource/index.html b/docs/api/types/Back/interfaces/AdminForthResource/index.html index a78265f37..891551346 100644 --- a/docs/api/types/Back/interfaces/AdminForthResource/index.html +++ b/docs/api/types/Back/interfaces/AdminForthResource/index.html @@ -15,7 +15,7 @@ - + diff --git a/docs/api/types/Back/interfaces/AdminForthResourceColumn/index.html b/docs/api/types/Back/interfaces/AdminForthResourceColumn/index.html index 95c68b0e6..a69c5ced0 100644 --- a/docs/api/types/Back/interfaces/AdminForthResourceColumn/index.html +++ b/docs/api/types/Back/interfaces/AdminForthResourceColumn/index.html @@ -15,7 +15,7 @@ - + diff --git a/docs/api/types/Back/interfaces/AdminForthResourceInput/index.html b/docs/api/types/Back/interfaces/AdminForthResourceInput/index.html index ee1538761..739143f00 100644 --- a/docs/api/types/Back/interfaces/AdminForthResourceInput/index.html +++ b/docs/api/types/Back/interfaces/AdminForthResourceInput/index.html @@ -15,7 +15,7 @@ - + diff --git a/docs/api/types/Back/interfaces/IAdminForth/index.html b/docs/api/types/Back/interfaces/IAdminForth/index.html index 225b826e1..cd02da329 100644 --- a/docs/api/types/Back/interfaces/IAdminForth/index.html +++ b/docs/api/types/Back/interfaces/IAdminForth/index.html @@ -15,7 +15,7 @@ - + diff --git a/docs/api/types/Back/interfaces/IAdminForthAuth/index.html b/docs/api/types/Back/interfaces/IAdminForthAuth/index.html index 68ebba55b..3c8fa7319 100644 --- a/docs/api/types/Back/interfaces/IAdminForthAuth/index.html +++ b/docs/api/types/Back/interfaces/IAdminForthAuth/index.html @@ -15,7 +15,7 @@ - + diff --git a/docs/api/types/Back/interfaces/IAdminForthDataSourceConnector/index.html b/docs/api/types/Back/interfaces/IAdminForthDataSourceConnector/index.html index c1952fc53..1c2fd4933 100644 --- a/docs/api/types/Back/interfaces/IAdminForthDataSourceConnector/index.html +++ b/docs/api/types/Back/interfaces/IAdminForthDataSourceConnector/index.html @@ -15,7 +15,7 @@ - + diff --git a/docs/api/types/Back/interfaces/IAdminForthDataSourceConnectorBase/index.html b/docs/api/types/Back/interfaces/IAdminForthDataSourceConnectorBase/index.html index 10a5df9ec..5e604b244 100644 --- a/docs/api/types/Back/interfaces/IAdminForthDataSourceConnectorBase/index.html +++ b/docs/api/types/Back/interfaces/IAdminForthDataSourceConnectorBase/index.html @@ -15,7 +15,7 @@ - + diff --git a/docs/api/types/Back/interfaces/IAdminForthDataSourceConnectorConstructor/index.html b/docs/api/types/Back/interfaces/IAdminForthDataSourceConnectorConstructor/index.html index 16fc52587..7e8d6b2aa 100644 --- a/docs/api/types/Back/interfaces/IAdminForthDataSourceConnectorConstructor/index.html +++ b/docs/api/types/Back/interfaces/IAdminForthDataSourceConnectorConstructor/index.html @@ -15,7 +15,7 @@ - + diff --git a/docs/api/types/Back/interfaces/IAdminForthFilter/index.html b/docs/api/types/Back/interfaces/IAdminForthFilter/index.html index bdb58d797..18805d644 100644 --- a/docs/api/types/Back/interfaces/IAdminForthFilter/index.html +++ b/docs/api/types/Back/interfaces/IAdminForthFilter/index.html @@ -15,7 +15,7 @@ - + diff --git a/docs/api/types/Back/interfaces/IAdminForthHttpResponse/index.html b/docs/api/types/Back/interfaces/IAdminForthHttpResponse/index.html index 6e5f075ca..c01e731aa 100644 --- a/docs/api/types/Back/interfaces/IAdminForthHttpResponse/index.html +++ b/docs/api/types/Back/interfaces/IAdminForthHttpResponse/index.html @@ -15,7 +15,7 @@ - + diff --git a/docs/api/types/Back/interfaces/IAdminForthPlugin/index.html b/docs/api/types/Back/interfaces/IAdminForthPlugin/index.html index 21e93a6ea..09bb66e69 100644 --- a/docs/api/types/Back/interfaces/IAdminForthPlugin/index.html +++ b/docs/api/types/Back/interfaces/IAdminForthPlugin/index.html @@ -15,7 +15,7 @@ - + diff --git a/docs/api/types/Back/interfaces/IAdminForthRestAPI/index.html b/docs/api/types/Back/interfaces/IAdminForthRestAPI/index.html index 5e511d0a7..b801fb867 100644 --- a/docs/api/types/Back/interfaces/IAdminForthRestAPI/index.html +++ b/docs/api/types/Back/interfaces/IAdminForthRestAPI/index.html @@ -15,7 +15,7 @@ - + diff --git a/docs/api/types/Back/interfaces/IAdminForthSort/index.html b/docs/api/types/Back/interfaces/IAdminForthSort/index.html index 008125a0c..4c1b387c3 100644 --- a/docs/api/types/Back/interfaces/IAdminForthSort/index.html +++ b/docs/api/types/Back/interfaces/IAdminForthSort/index.html @@ -15,7 +15,7 @@ - + diff --git a/docs/api/types/Back/interfaces/ICodeInjector/index.html b/docs/api/types/Back/interfaces/ICodeInjector/index.html index c2a1d63f2..8c0256af8 100644 --- a/docs/api/types/Back/interfaces/ICodeInjector/index.html +++ b/docs/api/types/Back/interfaces/ICodeInjector/index.html @@ -15,7 +15,7 @@ - + diff --git a/docs/api/types/Back/interfaces/IConfigValidator/index.html b/docs/api/types/Back/interfaces/IConfigValidator/index.html index 0425ea304..abe78ccb2 100644 --- a/docs/api/types/Back/interfaces/IConfigValidator/index.html +++ b/docs/api/types/Back/interfaces/IConfigValidator/index.html @@ -15,7 +15,7 @@ - + diff --git a/docs/api/types/Back/interfaces/IExpressHttpServer/index.html b/docs/api/types/Back/interfaces/IExpressHttpServer/index.html index 1c13a34cd..eeb73a524 100644 --- a/docs/api/types/Back/interfaces/IExpressHttpServer/index.html +++ b/docs/api/types/Back/interfaces/IExpressHttpServer/index.html @@ -15,7 +15,7 @@ - + diff --git a/docs/api/types/Back/interfaces/IHttpServer/index.html b/docs/api/types/Back/interfaces/IHttpServer/index.html index 41d1146ac..2c85348b3 100644 --- a/docs/api/types/Back/interfaces/IHttpServer/index.html +++ b/docs/api/types/Back/interfaces/IHttpServer/index.html @@ -15,7 +15,7 @@ - + diff --git a/docs/api/types/Back/interfaces/IOperationalResource/index.html b/docs/api/types/Back/interfaces/IOperationalResource/index.html index b88ceb459..63f928deb 100644 --- a/docs/api/types/Back/interfaces/IOperationalResource/index.html +++ b/docs/api/types/Back/interfaces/IOperationalResource/index.html @@ -15,7 +15,7 @@ - + diff --git a/docs/api/types/Back/interfaces/IWebSocketBroker/index.html b/docs/api/types/Back/interfaces/IWebSocketBroker/index.html index 410b295d3..e7c63765e 100644 --- a/docs/api/types/Back/interfaces/IWebSocketBroker/index.html +++ b/docs/api/types/Back/interfaces/IWebSocketBroker/index.html @@ -15,7 +15,7 @@ - + diff --git a/docs/api/types/Back/interfaces/IWebSocketClient/index.html b/docs/api/types/Back/interfaces/IWebSocketClient/index.html index 6745fc4dc..295a9473d 100644 --- a/docs/api/types/Back/interfaces/IWebSocketClient/index.html +++ b/docs/api/types/Back/interfaces/IWebSocketClient/index.html @@ -15,7 +15,7 @@ - + diff --git a/docs/api/types/Back/interfaces/ResourceOptions/index.html b/docs/api/types/Back/interfaces/ResourceOptions/index.html index d8cfcd476..2f62ea344 100644 --- a/docs/api/types/Back/interfaces/ResourceOptions/index.html +++ b/docs/api/types/Back/interfaces/ResourceOptions/index.html @@ -15,7 +15,7 @@ - + diff --git a/docs/api/types/Back/interfaces/ResourceOptionsInput/index.html b/docs/api/types/Back/interfaces/ResourceOptionsInput/index.html index 52fcdcf2b..05d07156a 100644 --- a/docs/api/types/Back/interfaces/ResourceOptionsInput/index.html +++ b/docs/api/types/Back/interfaces/ResourceOptionsInput/index.html @@ -15,7 +15,7 @@ - + diff --git a/docs/api/types/Back/type-aliases/AdminForthDataSource/index.html b/docs/api/types/Back/type-aliases/AdminForthDataSource/index.html index 983ad4011..ac6ec3b5c 100644 --- a/docs/api/types/Back/type-aliases/AdminForthDataSource/index.html +++ b/docs/api/types/Back/type-aliases/AdminForthDataSource/index.html @@ -15,7 +15,7 @@ - + diff --git a/docs/api/types/Back/type-aliases/AfterDataSourceResponseFunction/index.html b/docs/api/types/Back/type-aliases/AfterDataSourceResponseFunction/index.html index 4943ae0fa..f5413cd14 100644 --- a/docs/api/types/Back/type-aliases/AfterDataSourceResponseFunction/index.html +++ b/docs/api/types/Back/type-aliases/AfterDataSourceResponseFunction/index.html @@ -15,7 +15,7 @@ - + diff --git a/docs/api/types/Back/type-aliases/AfterSaveFunction/index.html b/docs/api/types/Back/type-aliases/AfterSaveFunction/index.html index 115433030..9a9863f51 100644 --- a/docs/api/types/Back/type-aliases/AfterSaveFunction/index.html +++ b/docs/api/types/Back/type-aliases/AfterSaveFunction/index.html @@ -15,7 +15,7 @@ - + diff --git a/docs/api/types/Back/type-aliases/AllowedActionValue/index.html b/docs/api/types/Back/type-aliases/AllowedActionValue/index.html index 55e3d033a..ad588183c 100644 --- a/docs/api/types/Back/type-aliases/AllowedActionValue/index.html +++ b/docs/api/types/Back/type-aliases/AllowedActionValue/index.html @@ -15,7 +15,7 @@ - + diff --git a/docs/api/types/Back/type-aliases/AllowedActions/index.html b/docs/api/types/Back/type-aliases/AllowedActions/index.html index 25c4f7c1a..87bd7b65a 100644 --- a/docs/api/types/Back/type-aliases/AllowedActions/index.html +++ b/docs/api/types/Back/type-aliases/AllowedActions/index.html @@ -15,7 +15,7 @@ - + diff --git a/docs/api/types/Back/type-aliases/AllowedActionsInput/index.html b/docs/api/types/Back/type-aliases/AllowedActionsInput/index.html index 7940eb3dc..6cdd0baa5 100644 --- a/docs/api/types/Back/type-aliases/AllowedActionsInput/index.html +++ b/docs/api/types/Back/type-aliases/AllowedActionsInput/index.html @@ -15,7 +15,7 @@ - + diff --git a/docs/api/types/Back/type-aliases/BeforeDataSourceRequestFunction/index.html b/docs/api/types/Back/type-aliases/BeforeDataSourceRequestFunction/index.html index f8b55b091..0cea4d9e7 100644 --- a/docs/api/types/Back/type-aliases/BeforeDataSourceRequestFunction/index.html +++ b/docs/api/types/Back/type-aliases/BeforeDataSourceRequestFunction/index.html @@ -15,7 +15,7 @@ - + diff --git a/docs/api/types/Back/type-aliases/BeforeLoginConfirmationFunction/index.html b/docs/api/types/Back/type-aliases/BeforeLoginConfirmationFunction/index.html index 61f5617ca..2cfc0c60d 100644 --- a/docs/api/types/Back/type-aliases/BeforeLoginConfirmationFunction/index.html +++ b/docs/api/types/Back/type-aliases/BeforeLoginConfirmationFunction/index.html @@ -15,7 +15,7 @@ - + diff --git a/docs/api/types/Back/type-aliases/BeforeSaveFunction/index.html b/docs/api/types/Back/type-aliases/BeforeSaveFunction/index.html index 7f1921f26..317aa6801 100644 --- a/docs/api/types/Back/type-aliases/BeforeSaveFunction/index.html +++ b/docs/api/types/Back/type-aliases/BeforeSaveFunction/index.html @@ -15,7 +15,7 @@ - + diff --git a/docs/api/types/Back/type-aliases/FDataFilter/index.html b/docs/api/types/Back/type-aliases/FDataFilter/index.html index fbb1f00e1..dcab006a2 100644 --- a/docs/api/types/Back/type-aliases/FDataFilter/index.html +++ b/docs/api/types/Back/type-aliases/FDataFilter/index.html @@ -15,7 +15,7 @@ - + diff --git a/docs/api/types/Back/type-aliases/FDataSort/index.html b/docs/api/types/Back/type-aliases/FDataSort/index.html index dd1a9af50..2dfc5edbe 100644 --- a/docs/api/types/Back/type-aliases/FDataSort/index.html +++ b/docs/api/types/Back/type-aliases/FDataSort/index.html @@ -15,7 +15,7 @@ - + diff --git a/docs/api/types/Common/enumerations/ActionCheckSource/index.html b/docs/api/types/Common/enumerations/ActionCheckSource/index.html index 4e1bce14b..59ec45bc7 100644 --- a/docs/api/types/Common/enumerations/ActionCheckSource/index.html +++ b/docs/api/types/Common/enumerations/ActionCheckSource/index.html @@ -15,12 +15,12 @@ - +

ActionCheckSource

Enumeration Members

-
Enumeration MemberValue
BulkActionRequest"bulkActionRequest"
CreateRequest"createRequest"
DeleteRequest"deleteRequest"
DisplayButtons"displayButtons"
EditRequest"editRequest"
ListRequest"listRequest"
ShowRequest"showRequest"
+
Enumeration MemberValue
BulkActionRequest"bulkActionRequest"
CreateRequest"createRequest"
DeleteRequest"deleteRequest"
DisplayButtons"displayButtons"
EditLoadRequest"editLoadRequest"
EditRequest"editRequest"
ListRequest"listRequest"
ShowRequest"showRequest"
\ No newline at end of file diff --git a/docs/api/types/Common/enumerations/AdminForthDataTypes/index.html b/docs/api/types/Common/enumerations/AdminForthDataTypes/index.html index 8ba8046d1..a33cb3f93 100644 --- a/docs/api/types/Common/enumerations/AdminForthDataTypes/index.html +++ b/docs/api/types/Common/enumerations/AdminForthDataTypes/index.html @@ -15,7 +15,7 @@ - + diff --git a/docs/api/types/Common/enumerations/AdminForthFilterOperators/index.html b/docs/api/types/Common/enumerations/AdminForthFilterOperators/index.html index da2947a8c..22f202a82 100644 --- a/docs/api/types/Common/enumerations/AdminForthFilterOperators/index.html +++ b/docs/api/types/Common/enumerations/AdminForthFilterOperators/index.html @@ -15,7 +15,7 @@ - + diff --git a/docs/api/types/Common/enumerations/AdminForthMenuTypes/index.html b/docs/api/types/Common/enumerations/AdminForthMenuTypes/index.html index 3f798077b..e91ccbce9 100644 --- a/docs/api/types/Common/enumerations/AdminForthMenuTypes/index.html +++ b/docs/api/types/Common/enumerations/AdminForthMenuTypes/index.html @@ -15,7 +15,7 @@ - + diff --git a/docs/api/types/Common/enumerations/AdminForthResourcePages/index.html b/docs/api/types/Common/enumerations/AdminForthResourcePages/index.html index ff12c379a..72872f2b5 100644 --- a/docs/api/types/Common/enumerations/AdminForthResourcePages/index.html +++ b/docs/api/types/Common/enumerations/AdminForthResourcePages/index.html @@ -15,7 +15,7 @@ - + diff --git a/docs/api/types/Common/enumerations/AdminForthSortDirections/index.html b/docs/api/types/Common/enumerations/AdminForthSortDirections/index.html index 8d3a525e9..221525f12 100644 --- a/docs/api/types/Common/enumerations/AdminForthSortDirections/index.html +++ b/docs/api/types/Common/enumerations/AdminForthSortDirections/index.html @@ -15,7 +15,7 @@ - + diff --git a/docs/api/types/Common/enumerations/AllowedActionsEnum/index.html b/docs/api/types/Common/enumerations/AllowedActionsEnum/index.html index 89d95b516..5ed96685a 100644 --- a/docs/api/types/Common/enumerations/AllowedActionsEnum/index.html +++ b/docs/api/types/Common/enumerations/AllowedActionsEnum/index.html @@ -15,7 +15,7 @@ - + diff --git a/docs/api/types/Common/index.html b/docs/api/types/Common/index.html index c419562be..e38590cb9 100644 --- a/docs/api/types/Common/index.html +++ b/docs/api/types/Common/index.html @@ -15,7 +15,7 @@ - + diff --git a/docs/api/types/Common/interfaces/AdminForthBulkActionCommon/index.html b/docs/api/types/Common/interfaces/AdminForthBulkActionCommon/index.html index 733adea8a..69f7420ae 100644 --- a/docs/api/types/Common/interfaces/AdminForthBulkActionCommon/index.html +++ b/docs/api/types/Common/interfaces/AdminForthBulkActionCommon/index.html @@ -15,7 +15,7 @@ - + diff --git a/docs/api/types/Common/interfaces/AdminForthColumnEnumItem/index.html b/docs/api/types/Common/interfaces/AdminForthColumnEnumItem/index.html index ca190b40c..543e37e90 100644 --- a/docs/api/types/Common/interfaces/AdminForthColumnEnumItem/index.html +++ b/docs/api/types/Common/interfaces/AdminForthColumnEnumItem/index.html @@ -15,7 +15,7 @@ - + diff --git a/docs/api/types/Common/interfaces/AdminForthComponentDeclarationFull/index.html b/docs/api/types/Common/interfaces/AdminForthComponentDeclarationFull/index.html index 3bbf4f235..011d71704 100644 --- a/docs/api/types/Common/interfaces/AdminForthComponentDeclarationFull/index.html +++ b/docs/api/types/Common/interfaces/AdminForthComponentDeclarationFull/index.html @@ -15,7 +15,7 @@ - + diff --git a/docs/api/types/Common/interfaces/AdminForthConfigForFrontend/index.html b/docs/api/types/Common/interfaces/AdminForthConfigForFrontend/index.html index f85670aa0..fa3fce34e 100644 --- a/docs/api/types/Common/interfaces/AdminForthConfigForFrontend/index.html +++ b/docs/api/types/Common/interfaces/AdminForthConfigForFrontend/index.html @@ -15,7 +15,7 @@ - + diff --git a/docs/api/types/Common/interfaces/AdminForthConfigMenuItem/index.html b/docs/api/types/Common/interfaces/AdminForthConfigMenuItem/index.html index 91cbe1eb8..94fd6bc87 100644 --- a/docs/api/types/Common/interfaces/AdminForthConfigMenuItem/index.html +++ b/docs/api/types/Common/interfaces/AdminForthConfigMenuItem/index.html @@ -15,7 +15,7 @@ - + diff --git a/docs/api/types/Common/interfaces/AdminForthFieldComponents/index.html b/docs/api/types/Common/interfaces/AdminForthFieldComponents/index.html index 8d1ba5563..956f4d231 100644 --- a/docs/api/types/Common/interfaces/AdminForthFieldComponents/index.html +++ b/docs/api/types/Common/interfaces/AdminForthFieldComponents/index.html @@ -15,7 +15,7 @@ - + diff --git a/docs/api/types/Common/interfaces/AdminForthForeignResourceCommon/index.html b/docs/api/types/Common/interfaces/AdminForthForeignResourceCommon/index.html index 6f6872164..0358a5679 100644 --- a/docs/api/types/Common/interfaces/AdminForthForeignResourceCommon/index.html +++ b/docs/api/types/Common/interfaces/AdminForthForeignResourceCommon/index.html @@ -15,7 +15,7 @@ - + diff --git a/docs/api/types/Common/interfaces/AdminForthResourceColumnCommon/index.html b/docs/api/types/Common/interfaces/AdminForthResourceColumnCommon/index.html index 51d7cf969..9a3a26d61 100644 --- a/docs/api/types/Common/interfaces/AdminForthResourceColumnCommon/index.html +++ b/docs/api/types/Common/interfaces/AdminForthResourceColumnCommon/index.html @@ -15,7 +15,7 @@ - + diff --git a/docs/api/types/Common/interfaces/AdminForthResourceColumnInputCommon/index.html b/docs/api/types/Common/interfaces/AdminForthResourceColumnInputCommon/index.html index 5c81c7224..2776b865b 100644 --- a/docs/api/types/Common/interfaces/AdminForthResourceColumnInputCommon/index.html +++ b/docs/api/types/Common/interfaces/AdminForthResourceColumnInputCommon/index.html @@ -15,7 +15,7 @@ - + diff --git a/docs/api/types/Common/interfaces/AdminForthResourceCommon/index.html b/docs/api/types/Common/interfaces/AdminForthResourceCommon/index.html index 4bd3b6d3e..aaa01468e 100644 --- a/docs/api/types/Common/interfaces/AdminForthResourceCommon/index.html +++ b/docs/api/types/Common/interfaces/AdminForthResourceCommon/index.html @@ -15,7 +15,7 @@ - + diff --git a/docs/api/types/Common/interfaces/AdminForthResourceInputCommon/index.html b/docs/api/types/Common/interfaces/AdminForthResourceInputCommon/index.html index 8d165dfd5..cce6f0d85 100644 --- a/docs/api/types/Common/interfaces/AdminForthResourceInputCommon/index.html +++ b/docs/api/types/Common/interfaces/AdminForthResourceInputCommon/index.html @@ -15,7 +15,7 @@ - + diff --git a/docs/api/types/Common/interfaces/AdminUser/index.html b/docs/api/types/Common/interfaces/AdminUser/index.html index 94edc45ec..df95c015e 100644 --- a/docs/api/types/Common/interfaces/AdminUser/index.html +++ b/docs/api/types/Common/interfaces/AdminUser/index.html @@ -15,7 +15,7 @@ - + diff --git a/docs/api/types/Common/interfaces/GetBaseConfigResponse/index.html b/docs/api/types/Common/interfaces/GetBaseConfigResponse/index.html index 15b3ddc5c..ef348970c 100644 --- a/docs/api/types/Common/interfaces/GetBaseConfigResponse/index.html +++ b/docs/api/types/Common/interfaces/GetBaseConfigResponse/index.html @@ -15,7 +15,7 @@ - + diff --git a/docs/api/types/Common/interfaces/ResourceVeryShort/index.html b/docs/api/types/Common/interfaces/ResourceVeryShort/index.html index ecf38364e..0b245abde 100644 --- a/docs/api/types/Common/interfaces/ResourceVeryShort/index.html +++ b/docs/api/types/Common/interfaces/ResourceVeryShort/index.html @@ -15,7 +15,7 @@ - + diff --git a/docs/api/types/Common/interfaces/UserData/index.html b/docs/api/types/Common/interfaces/UserData/index.html index 6b614f6ff..30d3b054a 100644 --- a/docs/api/types/Common/interfaces/UserData/index.html +++ b/docs/api/types/Common/interfaces/UserData/index.html @@ -15,7 +15,7 @@ - + diff --git a/docs/api/types/Common/type-aliases/AdminForthComponentDeclaration/index.html b/docs/api/types/Common/type-aliases/AdminForthComponentDeclaration/index.html index 05bb477ed..54d642374 100644 --- a/docs/api/types/Common/type-aliases/AdminForthComponentDeclaration/index.html +++ b/docs/api/types/Common/type-aliases/AdminForthComponentDeclaration/index.html @@ -15,7 +15,7 @@ - + diff --git a/docs/api/types/Common/type-aliases/AllowedActionsResolved/index.html b/docs/api/types/Common/type-aliases/AllowedActionsResolved/index.html index 4dce97cbf..d9bf0993f 100644 --- a/docs/api/types/Common/type-aliases/AllowedActionsResolved/index.html +++ b/docs/api/types/Common/type-aliases/AllowedActionsResolved/index.html @@ -15,7 +15,7 @@ - + diff --git a/docs/api/types/Common/type-aliases/AnnouncementBadgeResponse/index.html b/docs/api/types/Common/type-aliases/AnnouncementBadgeResponse/index.html index 8170620af..b2789a610 100644 --- a/docs/api/types/Common/type-aliases/AnnouncementBadgeResponse/index.html +++ b/docs/api/types/Common/type-aliases/AnnouncementBadgeResponse/index.html @@ -15,7 +15,7 @@ - + diff --git a/docs/api/types/Common/type-aliases/ValidationObject/index.html b/docs/api/types/Common/type-aliases/ValidationObject/index.html index a6fc357cd..554b90316 100644 --- a/docs/api/types/Common/type-aliases/ValidationObject/index.html +++ b/docs/api/types/Common/type-aliases/ValidationObject/index.html @@ -15,7 +15,7 @@ - + diff --git a/docs/api/types/FrontendAPI/enumerations/AlertVariant/index.html b/docs/api/types/FrontendAPI/enumerations/AlertVariant/index.html index f8eaa8ff3..a983c57ef 100644 --- a/docs/api/types/FrontendAPI/enumerations/AlertVariant/index.html +++ b/docs/api/types/FrontendAPI/enumerations/AlertVariant/index.html @@ -15,7 +15,7 @@ - + diff --git a/docs/api/types/FrontendAPI/index.html b/docs/api/types/FrontendAPI/index.html index dc635dfbd..b026daed2 100644 --- a/docs/api/types/FrontendAPI/index.html +++ b/docs/api/types/FrontendAPI/index.html @@ -15,7 +15,7 @@ - + diff --git a/docs/api/types/FrontendAPI/interfaces/FrontendAPIInterface/index.html b/docs/api/types/FrontendAPI/interfaces/FrontendAPIInterface/index.html index 5e7ee06ae..10578f3b2 100644 --- a/docs/api/types/FrontendAPI/interfaces/FrontendAPIInterface/index.html +++ b/docs/api/types/FrontendAPI/interfaces/FrontendAPIInterface/index.html @@ -15,7 +15,7 @@ - + diff --git a/docs/api/types/FrontendAPI/type-aliases/AlertParams/index.html b/docs/api/types/FrontendAPI/type-aliases/AlertParams/index.html index 7d7782dc3..655e16270 100644 --- a/docs/api/types/FrontendAPI/type-aliases/AlertParams/index.html +++ b/docs/api/types/FrontendAPI/type-aliases/AlertParams/index.html @@ -15,7 +15,7 @@ - + diff --git a/docs/api/types/FrontendAPI/type-aliases/ConfirmParams/index.html b/docs/api/types/FrontendAPI/type-aliases/ConfirmParams/index.html index 47aa7bf23..87226f7b5 100644 --- a/docs/api/types/FrontendAPI/type-aliases/ConfirmParams/index.html +++ b/docs/api/types/FrontendAPI/type-aliases/ConfirmParams/index.html @@ -15,7 +15,7 @@ - + diff --git a/docs/tutorial/Advanced/plugin-development/index.html b/docs/tutorial/Advanced/plugin-development/index.html index c8b9adb39..0f5e84d88 100644 --- a/docs/tutorial/Advanced/plugin-development/index.html +++ b/docs/tutorial/Advanced/plugin-development/index.html @@ -15,7 +15,7 @@ - + diff --git a/docs/tutorial/Customization/afcl/index.html b/docs/tutorial/Customization/afcl/index.html index 83d63bf10..48fd63f8b 100644 --- a/docs/tutorial/Customization/afcl/index.html +++ b/docs/tutorial/Customization/afcl/index.html @@ -15,7 +15,7 @@ - + @@ -42,15 +42,18 @@

MultipleCustom slots for item

<Select
:options="[
{label: 'Last 7 days', value: '7', records: 110},
{label: 'Last 30 days', value: '30', records: 320},
{label: 'Last 90 days', value: '90', records: 310},
{label: 'None', value: null}
]"
v-model="selected"
>
<template #item="{option}">
<div>
<span>{{ option.label }}</span>
<span class="ml-2 opacity-50">{{ option.records }} records</span>
</div>
</template>

<template #selected-item="{option}">
<span>{{ option.label }} 💫</span>
</template>
</Select>
</div>

alt text

-

Input

+

Extra item

+

You might need to put some extra item at bottom of list

+
<Select
:options="prices.map(price => ({label: price, value: price}))"
v-model="selected"
>
<template #extra-item>
<LinkButton to="/prices">Manage prices</LinkButton>
</template>
</Select>
+

Input


<Input type="number" class="w-full">
<template #suffix>
USD
</template>
</Input>

-

Tooltip

+

Tooltip

Wrap an element on which you would like to show a tooltip with the Tooltip component and add a tooltip slot to it.

import { Tooltip } from '@/afcl'
<Tooltip>
<a :href="`https://google.com?q=${record.title}`">
<IconCardSearch class="w-5 h-5 me-2"/>
</a>

<template #tooltip>
Search for competitive apartments in Google
</template>
</Tooltip>
-

VerticalTabs

+

VerticalTabs

Wrap each tab lable in tamplate with v-slot value tab:TAB_ALIAS. Wrap each tab content in tamplate with v-slot value TAB_ALIAS. TAB_ALIAS is a unique identifier for each tab here. Place all templates inside VerticalTabs component.

import { VerticalTabs } from '@/afcl'
import { IconGridSolid, IconUserCircleSolid } from '@iconify-prerendered/vue-flowbite';
-
  <VerticalTabs>
<template #tab:Profile>
<IconUserCircleSolid class="w-5 h-5 me-2"/>
Profile
</template>
<template #tab:Dashboard>
<IconGridSolid class="w-5 h-5 me-2"/>
Dashboard
</template>
<template #Profile>
<h3 class="text-lg font-bold text-gray-900 dark:text-white mb-2">Profile Tab</h3>
<p class="mb-2">This is some placeholder content the Profile tab's associated content, clicking another tab will toggle the visibility of this one for the next.</p>
</template>
<template #Dashboard>
Dashboard Tab Content
</template>
</VerticalTabs>
+
  <VerticalTabs>
<template #tab:Profile>
<IconUserCircleSolid class="w-5 h-5 me-2"/>
Profile
</template>
<template #tab:Dashboard>
<IconGridSolid class="w-5 h-5 me-2"/>
Dashboard
</template>
<template #Profile>
<h3 class="text-lg font-bold text-gray-900 dark:text-white mb-2">Profile Tab</h3>
<p class="mb-2">This is some placeholder content the Profile tab's associated content, clicking another tab will toggle the visibility of this one for the next.</p>
</template>
<template #Dashboard>
Dashboard Tab Content
</template>
</VerticalTabs>
\ No newline at end of file diff --git a/docs/tutorial/Customization/alert/index.html b/docs/tutorial/Customization/alert/index.html index 5849a6693..9b0e9fac1 100644 --- a/docs/tutorial/Customization/alert/index.html +++ b/docs/tutorial/Customization/alert/index.html @@ -15,7 +15,7 @@ - + diff --git a/docs/tutorial/Customization/branding/index.html b/docs/tutorial/Customization/branding/index.html index fb61d3f75..eb85a4b50 100644 --- a/docs/tutorial/Customization/branding/index.html +++ b/docs/tutorial/Customization/branding/index.html @@ -15,7 +15,7 @@ - + diff --git a/docs/tutorial/Customization/bulkActions/index.html b/docs/tutorial/Customization/bulkActions/index.html index 8a6002e80..fcb41aac4 100644 --- a/docs/tutorial/Customization/bulkActions/index.html +++ b/docs/tutorial/Customization/bulkActions/index.html @@ -15,7 +15,7 @@ - + diff --git a/docs/tutorial/Customization/customFieldRendering/index.html b/docs/tutorial/Customization/customFieldRendering/index.html index 8de8f3def..10f334eee 100644 --- a/docs/tutorial/Customization/customFieldRendering/index.html +++ b/docs/tutorial/Customization/customFieldRendering/index.html @@ -15,7 +15,7 @@ - + diff --git a/docs/tutorial/Customization/customPages/index.html b/docs/tutorial/Customization/customPages/index.html index a3a3d0008..2207d161a 100644 --- a/docs/tutorial/Customization/customPages/index.html +++ b/docs/tutorial/Customization/customPages/index.html @@ -15,7 +15,7 @@ - + diff --git a/docs/tutorial/Customization/dataApi/index.html b/docs/tutorial/Customization/dataApi/index.html index 19adbe3d1..df007847a 100644 --- a/docs/tutorial/Customization/dataApi/index.html +++ b/docs/tutorial/Customization/dataApi/index.html @@ -15,7 +15,7 @@ - + diff --git a/docs/tutorial/Customization/hooks/index.html b/docs/tutorial/Customization/hooks/index.html index 7105b2afc..408f39927 100644 --- a/docs/tutorial/Customization/hooks/index.html +++ b/docs/tutorial/Customization/hooks/index.html @@ -15,7 +15,7 @@ - + diff --git a/docs/tutorial/Customization/limitingAccess/index.html b/docs/tutorial/Customization/limitingAccess/index.html index 947e36f8f..d3824e411 100644 --- a/docs/tutorial/Customization/limitingAccess/index.html +++ b/docs/tutorial/Customization/limitingAccess/index.html @@ -15,7 +15,7 @@ - + @@ -49,8 +49,8 @@

./resources/apartments.ts
 allowedActions: {
...
show: async ({adminUser, meta, source, adminforth}: any) => {
if (source === 'showRequest' || source === 'editRequest') {
const record = await adminforth.resource('aparts').get(Filters.EQ('id', meta.pk));
return record.realtor_id === adminUser.dbUser.id;
}
return true;
},
}
+
./resources/apartments.ts
 allowedActions: {
...
show: async ({adminUser, meta, source, adminforth}: any) => {
if (source === 'showRequest' || source === 'editLoadRequest') {
const record = await adminforth.resource('aparts').get(Filters.EQ('id', meta.pk));
return record.realtor_id === adminUser.dbUser.id;
}
return true;
},
}

Please note that show callback is called not only when user visits show page (source will be 'showRequest' during this check) but also -when user visits edit page (source will be 'editRequest').

+when user visits edit page (source will be 'editLoadRequest').

\ No newline at end of file diff --git a/docs/tutorial/Customization/menuConfiguration/index.html b/docs/tutorial/Customization/menuConfiguration/index.html index 31a2d8225..adcf0ac38 100644 --- a/docs/tutorial/Customization/menuConfiguration/index.html +++ b/docs/tutorial/Customization/menuConfiguration/index.html @@ -15,7 +15,7 @@ - + diff --git a/docs/tutorial/Customization/pageInjections/index.html b/docs/tutorial/Customization/pageInjections/index.html index 8d4519768..e148b85ee 100644 --- a/docs/tutorial/Customization/pageInjections/index.html +++ b/docs/tutorial/Customization/pageInjections/index.html @@ -15,7 +15,7 @@ - + diff --git a/docs/tutorial/Customization/security/index.html b/docs/tutorial/Customization/security/index.html index 9c7efe4d7..6a6b3574c 100644 --- a/docs/tutorial/Customization/security/index.html +++ b/docs/tutorial/Customization/security/index.html @@ -15,7 +15,7 @@ - + diff --git a/docs/tutorial/Customization/standardPagesTuning/index.html b/docs/tutorial/Customization/standardPagesTuning/index.html index 28198361f..34344cd6e 100644 --- a/docs/tutorial/Customization/standardPagesTuning/index.html +++ b/docs/tutorial/Customization/standardPagesTuning/index.html @@ -15,7 +15,7 @@ - + @@ -54,9 +54,12 @@

Fil

Same effect can be achieved by using hooks. But fillOnCreate might be shorter and more readable.

+

+

Sometimes you might need to create a link that will open the create form with some fields pre-filled. For example, you might want to create a link that will open the create form with the realtor_id field pre-filled with the current user's ID.

+
<template>
...
<LinkButton
:to="{
name: 'resource-create',
params: {
resourceId: 'aparts',
},
query: {
values: encodeURIComponent(JSON.stringify({
realtor_id: coreStore?.adminUser.dbUser.id
})),
},
}"
>
Create new apartment
</LinkButton>
...
</template>

<script setup lang="ts">
import { LinkButton } from '@afcl';
import { useCoreStore } from '@/stores/core';

const coreStore = useCoreStore();
</script>

Editing

You can set a column editReadonly so it will be shown in the edit form but will be disabled.
This might be useful to better identify the record during editing or to show some additional information that should not be changed but can help to edit the record.

-
./resources/apartments.ts
new AdminForth({
...
resources: [
{
name: 'apartments',
fields: [
...
{
name: 'created_at',
type: AdminForthDataTypes.DATETIME,
editReadonly: true,
},
],
},
...
],
+
./resources/apartments.ts
new AdminForth({
...
resources: [
{
name: 'apartments',
fields: [
...
{
name: 'created_at',
type: AdminForthDataTypes.DATETIME,
editReadonly: true,
},
],
},
...
],
\ No newline at end of file diff --git a/docs/tutorial/Customization/virtualColumns/index.html b/docs/tutorial/Customization/virtualColumns/index.html index c1c04ac7a..6f936faea 100644 --- a/docs/tutorial/Customization/virtualColumns/index.html +++ b/docs/tutorial/Customization/virtualColumns/index.html @@ -15,7 +15,7 @@ - + diff --git a/docs/tutorial/Customization/websocket/index.html b/docs/tutorial/Customization/websocket/index.html index a4607d2fe..f35b90479 100644 --- a/docs/tutorial/Customization/websocket/index.html +++ b/docs/tutorial/Customization/websocket/index.html @@ -15,7 +15,7 @@ - + diff --git a/docs/tutorial/Plugins/AuditLog/index.html b/docs/tutorial/Plugins/AuditLog/index.html index a4fa28e7f..237e47ca9 100644 --- a/docs/tutorial/Plugins/AuditLog/index.html +++ b/docs/tutorial/Plugins/AuditLog/index.html @@ -15,7 +15,7 @@ - + diff --git a/docs/tutorial/Plugins/ForeignInlineList/index.html b/docs/tutorial/Plugins/ForeignInlineList/index.html index a44f8c762..f8db47223 100644 --- a/docs/tutorial/Plugins/ForeignInlineList/index.html +++ b/docs/tutorial/Plugins/ForeignInlineList/index.html @@ -15,7 +15,7 @@ - + diff --git a/docs/tutorial/Plugins/RichEditor/index.html b/docs/tutorial/Plugins/RichEditor/index.html index d39ae5230..eb83bfdd5 100644 --- a/docs/tutorial/Plugins/RichEditor/index.html +++ b/docs/tutorial/Plugins/RichEditor/index.html @@ -15,7 +15,7 @@ - + diff --git a/docs/tutorial/Plugins/TwoFactorsAuth/index.html b/docs/tutorial/Plugins/TwoFactorsAuth/index.html index 1c63d650b..55d88bece 100644 --- a/docs/tutorial/Plugins/TwoFactorsAuth/index.html +++ b/docs/tutorial/Plugins/TwoFactorsAuth/index.html @@ -15,7 +15,7 @@ - + diff --git a/docs/tutorial/Plugins/chat-gpt/index.html b/docs/tutorial/Plugins/chat-gpt/index.html index 28d306ccd..745ad19fe 100644 --- a/docs/tutorial/Plugins/chat-gpt/index.html +++ b/docs/tutorial/Plugins/chat-gpt/index.html @@ -15,7 +15,7 @@ - + diff --git a/docs/tutorial/Plugins/email-password-reset/index.html b/docs/tutorial/Plugins/email-password-reset/index.html index aec4d4bc4..f1ce6ac45 100644 --- a/docs/tutorial/Plugins/email-password-reset/index.html +++ b/docs/tutorial/Plugins/email-password-reset/index.html @@ -15,7 +15,7 @@ - + diff --git a/docs/tutorial/Plugins/import-export/index.html b/docs/tutorial/Plugins/import-export/index.html index 7ea212235..5c65faa0b 100644 --- a/docs/tutorial/Plugins/import-export/index.html +++ b/docs/tutorial/Plugins/import-export/index.html @@ -15,7 +15,7 @@ - + diff --git a/docs/tutorial/Plugins/open-signup/index.html b/docs/tutorial/Plugins/open-signup/index.html index 7138e19d5..d5d71441d 100644 --- a/docs/tutorial/Plugins/open-signup/index.html +++ b/docs/tutorial/Plugins/open-signup/index.html @@ -15,7 +15,7 @@ - + diff --git a/docs/tutorial/Plugins/upload/index.html b/docs/tutorial/Plugins/upload/index.html index edfb2ea94..cdb8454ca 100644 --- a/docs/tutorial/Plugins/upload/index.html +++ b/docs/tutorial/Plugins/upload/index.html @@ -15,7 +15,7 @@ - + diff --git a/docs/tutorial/deploy/index.html b/docs/tutorial/deploy/index.html index 5127304f8..79e43b7af 100644 --- a/docs/tutorial/deploy/index.html +++ b/docs/tutorial/deploy/index.html @@ -15,7 +15,7 @@ - + diff --git a/docs/tutorial/gettingStarted/index.html b/docs/tutorial/gettingStarted/index.html index 82beb6e2a..310872022 100644 --- a/docs/tutorial/gettingStarted/index.html +++ b/docs/tutorial/gettingStarted/index.html @@ -15,7 +15,7 @@ - + @@ -76,9 +76,9 @@

Database c
./index.ts
import express from 'express';
import AdminForth, { Filters } from 'adminforth';
import usersResource from "./resources/users";
import apartmentsResource from "./resources/apartments";


const ADMIN_BASE_URL = '';

export const admin = new AdminForth({
baseUrl : ADMIN_BASE_URL,
auth: {
usersResourceId: 'users', // resource to get user during login
usernameField: 'email', // field where username is stored, should exist in resource
passwordHashField: 'password_hash',
rememberMeDays: 30, // users who will check "remember me" will stay logged in for 30 days
},
customization: {
brandName: 'My Admin',
datesFormat: 'D MMM YY',
timeFormat: 'HH:mm:ss',
emptyFieldPlaceholder: '-',
},
dataSources: [
{
id: 'maindb',
url: `sqlite://${process.env.DATABASE_FILE}`
},
],
resources: [
apartmentsResource,
usersResource,
],
menu: [
{
label: 'Core',
icon: 'flowbite:brain-solid', // any icon from iconify supported in format <setname>:<icon>, e.g. from here https://icon-sets.iconify.design/flowbite/
open: true,
children: [
{
homepage: true,
label: 'Apartments',
icon: 'flowbite:home-solid',
resourceId: 'aparts',
},
]
},
{
type: 'gap'
},
{
type: 'divider'
},
{
type: 'heading',
label: 'SYSTEM',
},
{
label: 'Users',
icon: 'flowbite:user-solid',
resourceId: 'users',
}
],
});

if (import.meta.url === `file://${process.argv[1]}`) {
// if script is executed directly e.g. node index.ts or npm start


const app = express()
app.use(express.json());
const port = 3500;

// needed to compile SPA. Call it here or from a build script e.g. in Docker build time to reduce downtime
await admin.bundleNow({ hotReload: process.env.NODE_ENV === 'development'});
console.log('Bundling AdminForth done. For faster serving consider calling bundleNow() from a build script.');


// serve after you added all api
admin.express.serve(app)

admin.discoverDatabases().then(async () => {
if (!await admin.resource('users').get([Filters.EQ('email', 'adminforth')])) {
await admin.resource('users').create({
email: 'adminforth',
password_hash: await AdminForth.Utils.generatePasswordHash('adminforth'),
role: 'superadmin',
});
}
});

admin.express.listen(port, () => {
console.log(`Example app listening at http://localhost:${port}`)
console.log(`\n⚡ AdminForth is available at http://localhost:${port}${ADMIN_BASE_URL}\n`)
});
}

Next step you need to create resources folder.

Create apartments.ts in resources:

-
/apartments.ts
import { AdminForthDataTypes, AdminForthResource } from 'adminforth';

export default {
dataSource: 'maindb',
table: 'apartments',
resourceId: 'aparts', // resourceId is defaulted to table name but you can redefine it like this e.g.
// in case of same table names from different data sources
label: 'Apartments', // label is defaulted to table name but you can change it
recordLabel: (r) => `🏡 ${r.title}`,
columns: [
{
name: 'id',
label: 'Identifier', // if you wish you can redefine label, defaulted to uppercased name
showIn: ['filter', 'show'], // show column in filter and in show page
primaryKey: true,
fillOnCreate: ({ initialRecord, adminUser }) => Math.random().toString(36).substring(7), // called during creation to generate content of field, initialRecord is values user entered, adminUser object of user who creates record
},
{
name: 'title',
required: true,
showIn: ['list', 'create', 'edit', 'filter', 'show'], // all available options
maxLength: 255, // you can set max length for string fields
minLength: 3, // you can set min length for string fields
},
{
name: 'created_at',
type: AdminForthDataTypes.DATETIME,
allowMinMaxQuery: true,
showIn: ['list', 'filter', 'show', 'edit'],
fillOnCreate: ({ initialRecord, adminUser }) => (new Date()).toISOString(),
},
{
name: 'price',
allowMinMaxQuery: true, // use better experience for filtering e.g. date range, set it only if you have index on this column or if you sure there will be low number of rows
editingNote: 'Price is in USD', // you can put a note near field on editing or creating page
},
{
name: 'square_meter',
label: 'Square',
allowMinMaxQuery: true,
minValue: 1, // you can set min /max value for number columns so users will not be able to enter more/less
maxValue: 1000,
},
{
name: 'number_of_rooms',
allowMinMaxQuery: true,
enum: [
{ value: 1, label: '1 room' },
{ value: 2, label: '2 rooms' },
{ value: 3, label: '3 rooms' },
{ value: 4, label: '4 rooms' },
{ value: 5, label: '5 rooms' },
],
},
{
name: 'description',
sortable: false,
showIn: ['show', 'edit', 'create', 'filter'],
},
{
name: 'country',
enum: [{
value: 'US',
label: 'United States'
}, {
value: 'DE',
label: 'Germany'
}, {
value: 'FR',
label: 'France'
}, {
value: 'GB',
label: 'United Kingdom'
}, {
value: 'NL',
label: 'Netherlands'
}, {
value: 'IT',
label: 'Italy'
}, {
value: 'ES',
label: 'Spain'
}, {
value: 'DK',
label: 'Denmark'
}, {
value: 'PL',
label: 'Poland'
}, {
value: 'UA',
label: 'Ukraine'
}, {
value: null,
label: 'Not defined'
}],
},
{
name: 'listed',
required: true, // will be required on create/edit
},
{
name: 'realtor_id',
foreignResource: {
resourceId: 'users',
}
}
],
options: {
listPageSize: 12,
allowedActions: {
edit: true,
delete: true,
show: true,
filter: true,
},
},
} as AdminForthResource;
+
/apartments.ts
import { AdminForthDataTypes, AdminForthResourceInput } from 'adminforth';

export default {
dataSource: 'maindb',
table: 'apartments',
resourceId: 'aparts', // resourceId is defaulted to table name but you can redefine it like this e.g.
// in case of same table names from different data sources
label: 'Apartments', // label is defaulted to table name but you can change it
recordLabel: (r) => `🏡 ${r.title}`,
columns: [
{
name: 'id',
label: 'Identifier', // if you wish you can redefine label, defaulted to uppercased name
showIn: ['filter', 'show'], // show column in filter and in show page
primaryKey: true,
fillOnCreate: ({ initialRecord, adminUser }) => Math.random().toString(36).substring(7), // called during creation to generate content of field, initialRecord is values user entered, adminUser object of user who creates record
},
{
name: 'title',
required: true,
showIn: ['list', 'create', 'edit', 'filter', 'show'], // all available options
maxLength: 255, // you can set max length for string fields
minLength: 3, // you can set min length for string fields
},
{
name: 'created_at',
type: AdminForthDataTypes.DATETIME,
allowMinMaxQuery: true,
showIn: ['list', 'filter', 'show', 'edit'],
fillOnCreate: ({ initialRecord, adminUser }) => (new Date()).toISOString(),
},
{
name: 'price',
allowMinMaxQuery: true, // use better experience for filtering e.g. date range, set it only if you have index on this column or if you sure there will be low number of rows
editingNote: 'Price is in USD', // you can put a note near field on editing or creating page
},
{
name: 'square_meter',
label: 'Square',
allowMinMaxQuery: true,
minValue: 1, // you can set min /max value for number columns so users will not be able to enter more/less
maxValue: 1000,
},
{
name: 'number_of_rooms',
allowMinMaxQuery: true,
enum: [
{ value: 1, label: '1 room' },
{ value: 2, label: '2 rooms' },
{ value: 3, label: '3 rooms' },
{ value: 4, label: '4 rooms' },
{ value: 5, label: '5 rooms' },
],
},
{
name: 'description',
sortable: false,
showIn: ['show', 'edit', 'create', 'filter'],
},
{
name: 'country',
enum: [{
value: 'US',
label: 'United States'
}, {
value: 'DE',
label: 'Germany'
}, {
value: 'FR',
label: 'France'
}, {
value: 'GB',
label: 'United Kingdom'
}, {
value: 'NL',
label: 'Netherlands'
}, {
value: 'IT',
label: 'Italy'
}, {
value: 'ES',
label: 'Spain'
}, {
value: 'DK',
label: 'Denmark'
}, {
value: 'PL',
label: 'Poland'
}, {
value: 'UA',
label: 'Ukraine'
}, {
value: null,
label: 'Not defined'
}],
},
{
name: 'listed',
required: true, // will be required on create/edit
},
{
name: 'realtor_id',
foreignResource: {
resourceId: 'users',
}
}
],
options: {
listPageSize: 12,
allowedActions: {
edit: true,
delete: true,
show: true,
filter: true,
},
},
} as AdminForthResourceInput;

Create users.ts in resources:

-
/users.ts
import AdminForth, { AdminForthDataTypes, AdminForthResource } from 'adminforth';
export default {
dataSource: 'maindb',
table: 'users',
resourceId: 'users',
label: 'Users',
recordLabel: (r) => `👤 ${r.email}`,
columns: [
{
name: 'id',
primaryKey: true,
fillOnCreate: ({ initialRecord, adminUser }) => Math.random().toString(36).substring(7),
showIn: ['list', 'filter', 'show'],
},
{
name: 'email',
required: true,
isUnique: true,
validation: [
// you can also use AdminForth.Utils.EMAIL_VALIDATOR which is alias to this object
{
regExp: '^[a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+\\.[a-zA-Z]{2,}$',
message: 'Email is not valid, must be in format example@test.com'
},
]
},
{
name: 'created_at',
type: AdminForthDataTypes.DATETIME,
showIn: ['list', 'filter', 'show'],
fillOnCreate: ({ initialRecord, adminUser }) => (new Date()).toISOString(),
},
{
name: 'role',
enum: [
{ value: 'superadmin', label: 'Super Admin' },
{ value: 'user', label: 'User' },
]
},
{
name: 'password',
virtual: true, // field will not be persisted into db
required: { create: true }, // make required only on create page
editingNote: { edit: 'Leave empty to keep password unchanged' },
type: AdminForthDataTypes.STRING,
showIn: ['create', 'edit'], // to show field only on create and edit pages
masked: true, // to show stars in input field

minLength: 8,
validation: [
// request to have at least 1 digit, 1 upper case, 1 lower case
AdminForth.Utils.PASSWORD_VALIDATORS.UP_LOW_NUM,
],
},
{ name: 'password_hash', backendOnly: true, showIn: [] }
],
hooks: {
create: {
beforeSave: async ({ record, adminUser, resource }) => {
record.password_hash = await AdminForth.Utils.generatePasswordHash(record.password);
return { ok: true };
}
},
edit: {
beforeSave: async ({ record, adminUser, resource }) => {
if (record.password) {
record.password_hash = await AdminForth.Utils.generatePasswordHash(record.password);
}
return { ok: true }
},
},
}
} as AdminForthResource;
+
/users.ts
import AdminForth, { AdminForthDataTypes, AdminForthResourceInput } from 'adminforth';
export default {
dataSource: 'maindb',
table: 'users',
resourceId: 'users',
label: 'Users',
recordLabel: (r) => `👤 ${r.email}`,
columns: [
{
name: 'id',
primaryKey: true,
fillOnCreate: ({ initialRecord, adminUser }) => Math.random().toString(36).substring(7),
showIn: ['list', 'filter', 'show'],
},
{
name: 'email',
required: true,
isUnique: true,
validation: [
// you can also use AdminForth.Utils.EMAIL_VALIDATOR which is alias to this object
{
regExp: '^[a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+\\.[a-zA-Z]{2,}$',
message: 'Email is not valid, must be in format example@test.com'
},
]
},
{
name: 'created_at',
type: AdminForthDataTypes.DATETIME,
showIn: ['list', 'filter', 'show'],
fillOnCreate: ({ initialRecord, adminUser }) => (new Date()).toISOString(),
},
{
name: 'role',
enum: [
{ value: 'superadmin', label: 'Super Admin' },
{ value: 'user', label: 'User' },
]
},
{
name: 'password',
virtual: true, // field will not be persisted into db
required: { create: true }, // make required only on create page
editingNote: { edit: 'Leave empty to keep password unchanged' },
type: AdminForthDataTypes.STRING,
showIn: ['create', 'edit'], // to show field only on create and edit pages
masked: true, // to show stars in input field

minLength: 8,
validation: [
// request to have at least 1 digit, 1 upper case, 1 lower case
AdminForth.Utils.PASSWORD_VALIDATORS.UP_LOW_NUM,
],
},
{ name: 'password_hash', backendOnly: true, showIn: [] }
],
hooks: {
create: {
beforeSave: async ({ record, adminUser, resource }) => {
record.password_hash = await AdminForth.Utils.generatePasswordHash(record.password);
return { ok: true };
}
},
edit: {
beforeSave: async ({ record, adminUser, resource }) => {
if (record.password) {
record.password_hash = await AdminForth.Utils.generatePasswordHash(record.password);
}
return { ok: true }
},
},
}
} as AdminForthResourceInput;

Now you can run your app:

npm start

Open http://localhost:3500 in your browser and login with credentials adminforth / adminforth.

diff --git a/docs/tutorial/glossary/index.html b/docs/tutorial/glossary/index.html index ed09ea329..e4f6058e4 100644 --- a/docs/tutorial/glossary/index.html +++ b/docs/tutorial/glossary/index.html @@ -15,7 +15,7 @@ - + diff --git a/docs/tutorial/helloWorld/index.html b/docs/tutorial/helloWorld/index.html index a1f45f621..198e6d27e 100644 --- a/docs/tutorial/helloWorld/index.html +++ b/docs/tutorial/helloWorld/index.html @@ -15,7 +15,7 @@ - + diff --git a/index.html b/index.html index 2edd59648..29683e55b 100644 --- a/index.html +++ b/index.html @@ -15,7 +15,7 @@ - + diff --git a/search/index.html b/search/index.html index ce8bde978..cb0dac63e 100644 --- a/search/index.html +++ b/search/index.html @@ -15,7 +15,7 @@ - +