diff --git a/404.html b/404.html index 605a04fe7..010a77a12 100644 --- a/404.html +++ b/404.html @@ -13,7 +13,7 @@ - + diff --git a/assets/images/localhost_3500_resource_audit_logs-8996765a6d534b48c3e52b9949e4cc89.png b/assets/images/localhost_3500_resource_audit_logs-8996765a6d534b48c3e52b9949e4cc89.png new file mode 100644 index 000000000..c7c0f42ef Binary files /dev/null and b/assets/images/localhost_3500_resource_audit_logs-8996765a6d534b48c3e52b9949e4cc89.png differ diff --git a/assets/js/0058754d.531c2617.js b/assets/js/0058754d.5a65823b.js similarity index 96% rename from assets/js/0058754d.531c2617.js rename to assets/js/0058754d.5a65823b.js index 1ab407dd4..965a34e83 100644 --- a/assets/js/0058754d.531c2617.js +++ b/assets/js/0058754d.5a65823b.js @@ -1 +1 @@ -"use strict";(self.webpackChunkadminforth=self.webpackChunkadminforth||[]).push([[3800],{2681:(e,n,i)=>{i.r(n),i.d(n,{assets:()=>a,contentTitle:()=>r,default:()=>d,frontMatter:()=>o,metadata:()=>l,toc:()=>c});var s=i(4848),t=i(8453);const o={},r=void 0,l={id:"tutorial/Plugins/ForeignInlineList",title:"ForeignInlineList",description:"Foreign inline list plugin allows to display a list (table) of items from a foreign table in the show view.",source:"@site/docs/tutorial/Plugins/ForeignInlineList.md",sourceDirName:"tutorial/Plugins",slug:"/tutorial/Plugins/ForeignInlineList",permalink:"/docs/tutorial/Plugins/ForeignInlineList",draft:!1,unlisted:!1,tags:[],version:"current",frontMatter:{},sidebar:"tutorialSidebar",previous:{title:"AuditLog",permalink:"/docs/tutorial/Plugins/AuditLog"}},a={},c=[{value:"Usage",id:"usage",level:2}];function u(e){const n={a:"a",code:"code",h2:"h2",img:"img",p:"p",pre:"pre",...(0,t.R)(),...e.components};return(0,s.jsxs)(s.Fragment,{children:[(0,s.jsx)(n.p,{children:"Foreign inline list plugin allows to display a list (table) of items from a foreign table in the show view."}),"\n",(0,s.jsx)(n.h2,{id:"usage",children:"Usage"}),"\n",(0,s.jsx)(n.p,{children:"Import plugin:"}),"\n",(0,s.jsx)(n.pre,{children:(0,s.jsx)(n.code,{className:"language-ts",children:"import ForeignInlineListPlugin from 'adminforth/plugins/ForeignInlineListPlugin';\n"})}),"\n",(0,s.jsx)(n.p,{children:"If you are using pure Node without TypeScript, you can use the following code:"}),"\n",(0,s.jsx)(n.pre,{children:(0,s.jsx)(n.code,{className:"language-js",children:"import ForeignInlineListPlugin from 'adminforth/dist/plugins/ForeignInlineListPlugin/index.js';\n"})}),"\n",(0,s.jsxs)(n.p,{children:["In ",(0,s.jsx)(n.a,{href:"/docs/tutorial/gettingStarted",children:"Getting Started"})," we created a ",(0,s.jsx)(n.code,{children:"'aparts'"})," resource which has a field ",(0,s.jsx)(n.code,{children:"'realtor_id'"}),".\nThis field refers to record from ",(0,s.jsx)(n.code,{children:"'users'"})," resource. This means that we can display a list of appartments in the user's show view."]}),"\n",(0,s.jsxs)(n.p,{children:["Add to your ",(0,s.jsx)(n.code,{children:"'users'"})," resource configuration (which we created in ), plugin instance:"]}),"\n",(0,s.jsx)(n.pre,{children:(0,s.jsx)(n.code,{className:"language-ts",children:"{ \n ...\n resourceId: 'users',\n ...\n plugins: [\n new ForeignInlineListPlugin({\n foreignResourceId: 'aparts',\n modifyTableResourceConfig: (resourceConfig: AdminForthResource) => {\n // hide column 'square_meter' from both 'list' and 'filter'\n const column = resourceConfig.columns.find((c: AdminForthResourceColumn) => c.name === 'square_meter')!.showIn = [];\n resourceConfig.options!.listPageSize = 1;\n\n // feel free to console.log and edit resourceConfig as you need\n },\n }),\n ],\n}\n"})}),"\n",(0,s.jsxs)(n.p,{children:["You can use ",(0,s.jsx)(n.code,{children:"modifyTableResourceConfig"})," callback to modify what columns to show in the list and filter of the foreign table."]}),"\n",(0,s.jsx)(n.p,{children:(0,s.jsx)(n.img,{alt:"alt text",src:i(9742).A+"",width:"3670",height:"2044"})}),"\n",(0,s.jsxs)(n.p,{children:["See ",(0,s.jsx)(n.a,{href:"/docs/api/plugins/ForeignInlineListPlugin/types/type-aliases/PluginOptions",children:"API Reference"})," for more all options."]})]})}function d(e={}){const{wrapper:n}={...(0,t.R)(),...e.components};return n?(0,s.jsx)(n,{...e,children:(0,s.jsx)(u,{...e})}):u(e)}},9742:(e,n,i)=>{i.d(n,{A:()=>s});const s=i.p+"assets/images/localhost_3500_resource_users_show_08dpfh-a8f5bd63bb09e071c67a13d7121e95fd.png"},8453:(e,n,i)=>{i.d(n,{R:()=>r,x:()=>l});var s=i(6540);const t={},o=s.createContext(t);function r(e){const n=s.useContext(o);return s.useMemo((function(){return"function"==typeof e?e(n):{...n,...e}}),[n,e])}function l(e){let n;return n=e.disableParentContext?"function"==typeof e.components?e.components(t):e.components||t:r(e.components),s.createElement(o.Provider,{value:n},e.children)}}}]); \ No newline at end of file +"use strict";(self.webpackChunkadminforth=self.webpackChunkadminforth||[]).push([[3800],{2681:(e,n,i)=>{i.r(n),i.d(n,{assets:()=>a,contentTitle:()=>r,default:()=>d,frontMatter:()=>o,metadata:()=>l,toc:()=>c});var s=i(4848),t=i(8453);const o={},r=void 0,l={id:"tutorial/Plugins/ForeignInlineList",title:"ForeignInlineList",description:"Foreign inline list plugin allows to display a list (table) of items from a foreign table in the show view.",source:"@site/docs/tutorial/Plugins/ForeignInlineList.md",sourceDirName:"tutorial/Plugins",slug:"/tutorial/Plugins/ForeignInlineList",permalink:"/docs/tutorial/Plugins/ForeignInlineList",draft:!1,unlisted:!1,tags:[],version:"current",frontMatter:{},sidebar:"tutorialSidebar",previous:{title:"AuditLog",permalink:"/docs/tutorial/Plugins/AuditLog"}},a={},c=[{value:"Usage",id:"usage",level:2}];function u(e){const n={a:"a",code:"code",h2:"h2",img:"img",p:"p",pre:"pre",...(0,t.R)(),...e.components};return(0,s.jsxs)(s.Fragment,{children:[(0,s.jsx)(n.p,{children:"Foreign inline list plugin allows to display a list (table) of items from a foreign table in the show view."}),"\n",(0,s.jsx)(n.h2,{id:"usage",children:"Usage"}),"\n",(0,s.jsx)(n.p,{children:"Import plugin:"}),"\n",(0,s.jsx)(n.pre,{children:(0,s.jsx)(n.code,{className:"language-ts",children:"import ForeignInlineListPlugin from 'adminforth/plugins/ForeignInlineListPlugin';\n"})}),"\n",(0,s.jsx)(n.p,{children:"If you are using pure Node without TypeScript, you can use the following code:"}),"\n",(0,s.jsx)(n.pre,{children:(0,s.jsx)(n.code,{className:"language-js",children:"import ForeignInlineListPlugin from 'adminforth/dist/plugins/ForeignInlineListPlugin/index.js';\n"})}),"\n",(0,s.jsxs)(n.p,{children:["In ",(0,s.jsx)(n.a,{href:"/docs/tutorial/gettingStarted",children:"Getting Started"})," we created a ",(0,s.jsx)(n.code,{children:"'aparts'"})," resource which has a field ",(0,s.jsx)(n.code,{children:"'realtor_id'"}),".\nThis field refers to record from ",(0,s.jsx)(n.code,{children:"'users'"})," resource. This means that we can display a list of appartments in the user's show view."]}),"\n",(0,s.jsxs)(n.p,{children:["Add to your ",(0,s.jsx)(n.code,{children:"'users'"})," resource configuration (which we created in ), plugin instance:"]}),"\n",(0,s.jsx)(n.pre,{children:(0,s.jsx)(n.code,{className:"language-ts",children:"{ \n ...\n resourceId: 'users',\n ...\n plugins: [\n new ForeignInlineListPlugin({\n foreignResourceId: 'aparts',\n modifyTableResourceConfig: (resourceConfig: AdminForthResource) => {\n // hide column 'square_meter' from both 'list' and 'filter'\n const column = resourceConfig.columns.find((c: AdminForthResourceColumn) => c.name === 'square_meter')!.showIn = [];\n resourceConfig.options!.listPageSize = 1;\n\n // feel free to console.log and edit resourceConfig as you need\n },\n }),\n ],\n}\n"})}),"\n",(0,s.jsxs)(n.p,{children:["You can use ",(0,s.jsx)(n.code,{children:"modifyTableResourceConfig"})," callback to modify what columns to show in the list and filter of the foreign table."]}),"\n",(0,s.jsx)(n.p,{children:(0,s.jsx)(n.img,{alt:"alt text",src:i(3986).A+"",width:"3670",height:"2044"})}),"\n",(0,s.jsxs)(n.p,{children:["See ",(0,s.jsx)(n.a,{href:"/docs/api/plugins/ForeignInlineListPlugin/types/type-aliases/PluginOptions",children:"API Reference"})," for more all options."]})]})}function d(e={}){const{wrapper:n}={...(0,t.R)(),...e.components};return n?(0,s.jsx)(n,{...e,children:(0,s.jsx)(u,{...e})}):u(e)}},3986:(e,n,i)=>{i.d(n,{A:()=>s});const s=i.p+"assets/images/localhost_3500_resource_users_show_08dpfh-a8f5bd63bb09e071c67a13d7121e95fd.png"},8453:(e,n,i)=>{i.d(n,{R:()=>r,x:()=>l});var s=i(6540);const t={},o=s.createContext(t);function r(e){const n=s.useContext(o);return s.useMemo((function(){return"function"==typeof e?e(n):{...n,...e}}),[n,e])}function l(e){let n;return n=e.disableParentContext?"function"==typeof e.components?e.components(t):e.components||t:r(e.components),s.createElement(o.Provider,{value:n},e.children)}}}]); \ No newline at end of file diff --git a/assets/js/024de627.c84afb60.js b/assets/js/024de627.8360fc62.js similarity index 58% rename from assets/js/024de627.c84afb60.js rename to assets/js/024de627.8360fc62.js index 56fc40a45..439036455 100644 --- a/assets/js/024de627.c84afb60.js +++ b/assets/js/024de627.8360fc62.js @@ -1 +1 @@ -"use strict";(self.webpackChunkadminforth=self.webpackChunkadminforth||[]).push([[7865],{5191:(e,t,n)=>{n.r(t),n.d(t,{assets:()=>d,contentTitle:()=>r,default:()=>u,frontMatter:()=>i,metadata:()=>a,toc:()=>c});var o=n(4848),s=n(8453);const i={},r="Hooks",a={id:"tutorial/Customization/hooks",title:"Hooks",description:"Hooks are used to:",source:"@site/docs/tutorial/03-Customization/04-hooks.md",sourceDirName:"tutorial/03-Customization",slug:"/tutorial/Customization/hooks",permalink:"/docs/tutorial/Customization/hooks",draft:!1,unlisted:!1,tags:[],version:"current",sidebarPosition:4,frontMatter:{},sidebar:"tutorialSidebar",previous:{title:"Virtual columns",permalink:"/docs/tutorial/Customization/virtualColumns"},next:{title:"Disabling actions",permalink:"/docs/tutorial/Customization/limitingAccess"}},d={},c=[{value:"Modify the data before it is saved to the database",id:"modify-the-data-before-it-is-saved-to-the-database",level:2}];function l(e){const t={a:"a",code:"code",h1:"h1",h2:"h2",li:"li",p:"p",pre:"pre",ul:"ul",...(0,s.R)(),...e.components};return(0,o.jsxs)(o.Fragment,{children:[(0,o.jsx)(t.h1,{id:"hooks",children:"Hooks"}),"\n",(0,o.jsx)(t.p,{children:"Hooks are used to:"}),"\n",(0,o.jsxs)(t.ul,{children:["\n",(0,o.jsx)(t.li,{children:"modify the data before it is saved to the database on create or update"}),"\n",(0,o.jsx)(t.li,{children:"execute something after data were saved or deleted"}),"\n",(0,o.jsx)(t.li,{children:"change the query before fetching items from the database"}),"\n",(0,o.jsx)(t.li,{children:"modify the fetched data before it is displayed in the list and show"}),"\n",(0,o.jsxs)(t.li,{children:["prevent the request to db depending on some condition (Better use ",(0,o.jsx)(t.a,{href:"#limiting-access-to-the-resource-actions",children:"allowedActions"})," for this)"]}),"\n"]}),"\n",(0,o.jsx)(t.h2,{id:"modify-the-data-before-it-is-saved-to-the-database",children:"Modify the data before it is saved to the database"}),"\n",(0,o.jsxs)(t.p,{children:["Let's add reference to ",(0,o.jsx)(t.code,{children:"adminUser"})," when user creates a new apartment:"]}),"\n",(0,o.jsx)(t.pre,{children:(0,o.jsx)(t.code,{className:"language-ts",metastring:"title='./index.ts'",children:"// diff-add\nimport type { AdminUser } from 'adminforth/types/AdminForthConfig.js';\n\n{\n ...\n resourceId: 'aparts',\n columns: [\n ...\n {\n name: 'realtor_id',\n ...\n+ showIn: ['list', 'show', 'edit'], // don't even show this field in create\n ...\n },\n ...\n ],\n ...\n+ hooks: {\n+ create: {\n+ beforeSave: async ({ adminUser, record }: { adminUser: AdminUser, record: any }) => {\n+ if (adminUser.isRoot) {\n+ return { ok: false, error: \"Root user can't create appartment, relogin as DB user\" };\n+ }\n+ record.realtor_id = adminUser.dbUser.id;\n+ return { ok: true, record };\n+ }\n+ }\n+ }\n}\n"})})]})}function u(e={}){const{wrapper:t}={...(0,s.R)(),...e.components};return t?(0,o.jsx)(t,{...e,children:(0,o.jsx)(l,{...e})}):l(e)}},8453:(e,t,n)=>{n.d(t,{R:()=>r,x:()=>a});var o=n(6540);const s={},i=o.createContext(s);function r(e){const t=o.useContext(i);return o.useMemo((function(){return"function"==typeof e?e(t):{...t,...e}}),[t,e])}function a(e){let t;return t=e.disableParentContext?"function"==typeof e.components?e.components(s):e.components||s:r(e.components),o.createElement(i.Provider,{value:t},e.children)}}}]); \ No newline at end of file +"use strict";(self.webpackChunkadminforth=self.webpackChunkadminforth||[]).push([[7865],{5191:(e,t,n)=>{n.r(t),n.d(t,{assets:()=>r,contentTitle:()=>a,default:()=>f,frontMatter:()=>s,metadata:()=>d,toc:()=>c});var o=n(4848),i=n(8453);const s={},a="Hooks",d={id:"tutorial/Customization/hooks",title:"Hooks",description:"Hooks are used to:",source:"@site/docs/tutorial/03-Customization/04-hooks.md",sourceDirName:"tutorial/03-Customization",slug:"/tutorial/Customization/hooks",permalink:"/docs/tutorial/Customization/hooks",draft:!1,unlisted:!1,tags:[],version:"current",sidebarPosition:4,frontMatter:{},sidebar:"tutorialSidebar",previous:{title:"Virtual columns",permalink:"/docs/tutorial/Customization/virtualColumns"},next:{title:"Disabling actions",permalink:"/docs/tutorial/Customization/limitingAccess"}},r={},c=[{value:"Modify the data before it is saved to the database",id:"modify-the-data-before-it-is-saved-to-the-database",level:2}];function l(e){const t={a:"a",code:"code",h1:"h1",h2:"h2",li:"li",p:"p",pre:"pre",ul:"ul",...(0,i.R)(),...e.components};return(0,o.jsxs)(o.Fragment,{children:[(0,o.jsx)(t.h1,{id:"hooks",children:"Hooks"}),"\n",(0,o.jsx)(t.p,{children:"Hooks are used to:"}),"\n",(0,o.jsxs)(t.ul,{children:["\n",(0,o.jsx)(t.li,{children:"modify the data before it is saved to the database on create or update"}),"\n",(0,o.jsx)(t.li,{children:"execute something after data were saved or deleted"}),"\n",(0,o.jsx)(t.li,{children:"change the query before fetching items from the database"}),"\n",(0,o.jsx)(t.li,{children:"modify the fetched data before it is displayed in the list and show"}),"\n",(0,o.jsxs)(t.li,{children:["prevent the request to db depending on some condition (Better use ",(0,o.jsx)(t.a,{href:"#limiting-access-to-the-resource-actions",children:"allowedActions"})," for this)"]}),"\n"]}),"\n",(0,o.jsx)(t.h2,{id:"modify-the-data-before-it-is-saved-to-the-database",children:"Modify the data before it is saved to the database"}),"\n",(0,o.jsxs)(t.p,{children:["Let's add reference to ",(0,o.jsx)(t.code,{children:"adminUser"})," when user creates a new apartment:"]}),"\n",(0,o.jsx)(t.pre,{children:(0,o.jsx)(t.code,{className:"language-ts",metastring:"title='./index.ts'",children:"// diff-add\nimport type { AdminUser } from 'adminforth/types/AdminForthConfig.js';\n\n{\n ...\n resourceId: 'aparts',\n columns: [\n ...\n {\n name: 'realtor_id',\n ...\n//diff-add\n showIn: ['list', 'show', 'edit'], // don't even show this field in create\n ...\n },\n ...\n ],\n ...\n//diff-add\n hooks: {\n//diff-add\n create: {\n//diff-add\n beforeSave: async ({ adminUser, record }: { adminUser: AdminUser, record: any }) => {\n//diff-add\n if (adminUser.isRoot) {\n//diff-add\n return { ok: false, error: \"Root user can't create appartment, relogin as DB user\" };\n//diff-add\n }\n//diff-add\n record.realtor_id = adminUser.dbUser.id;\n//diff-add\n return { ok: true, record };\n//diff-add\n }\n//diff-add\n }\n//diff-add\n }\n}\n"})})]})}function f(e={}){const{wrapper:t}={...(0,i.R)(),...e.components};return t?(0,o.jsx)(t,{...e,children:(0,o.jsx)(l,{...e})}):l(e)}},8453:(e,t,n)=>{n.d(t,{R:()=>a,x:()=>d});var o=n(6540);const i={},s=o.createContext(i);function a(e){const t=o.useContext(s);return o.useMemo((function(){return"function"==typeof e?e(t):{...t,...e}}),[t,e])}function d(e){let t;return t=e.disableParentContext?"function"==typeof e.components?e.components(i):e.components||i:a(e.components),o.createElement(s.Provider,{value:t},e.children)}}}]); \ No newline at end of file diff --git a/assets/js/0f6f7c87.a199deca.js b/assets/js/0f6f7c87.a199deca.js deleted file mode 100644 index 0c3fe881d..000000000 --- a/assets/js/0f6f7c87.a199deca.js +++ /dev/null @@ -1 +0,0 @@ -"use strict";(self.webpackChunkadminforth=self.webpackChunkadminforth||[]).push([[1768],{9706:(n,e,t)=>{t.r(e),t.d(e,{assets:()=>c,contentTitle:()=>r,default:()=>u,frontMatter:()=>s,metadata:()=>i,toc:()=>d});var o=t(4848),a=t(8453);const s={},r="Page Injections",i={id:"tutorial/Customization/pageInjections",title:"Page Injections",description:"In addition to ability to create custom pages and overwrite how fields are rendered, you can also inject custom components in standard AdminForth page.",source:"@site/docs/tutorial/03-Customization/08-pageInjections.md",sourceDirName:"tutorial/03-Customization",slug:"/tutorial/Customization/pageInjections",permalink:"/docs/tutorial/Customization/pageInjections",draft:!1,unlisted:!1,tags:[],version:"current",sidebarPosition:8,frontMatter:{},sidebar:"tutorialSidebar",previous:{title:"Alerts and confirmations",permalink:"/docs/tutorial/Customization/alert"},next:{title:"Custom bulk actions",permalink:"/docs/tutorial/Customization/bulkActions"}},c={},d=[];function l(n){const e={blockquote:"blockquote",code:"code",h1:"h1",img:"img",p:"p",pre:"pre",...(0,a.R)(),...n.components};return(0,o.jsxs)(o.Fragment,{children:[(0,o.jsx)(e.h1,{id:"page-injections",children:"Page Injections"}),"\n",(0,o.jsx)(e.p,{children:"In addition to ability to create custom pages and overwrite how fields are rendered, you can also inject custom components in standard AdminForth page."}),"\n",(0,o.jsxs)(e.p,{children:["For example let's add a custom pie chart to the ",(0,o.jsx)(e.code,{children:"list"})," page of the ",(0,o.jsx)(e.code,{children:"aparts"})," resource. Pie chart will show the distribution of the rooms count and more over will allow to filter the list by the rooms count."]}),"\n",(0,o.jsx)(e.pre,{children:(0,o.jsx)(e.code,{className:"language-ts",metastring:'title="/index.ts"',children:"{\n resourceId: 'aparts',\n ...\n//diff-add\n options: {\n//diff-add\n pageInjections: {\n//diff-add\n list: {\n//diff-add\n afterBreadcrumbs: '@@/ApartsPie.vue',\n//diff-add\n }\n//diff-add\n } \n//diff-add\n }\n}\n"})}),"\n",(0,o.jsxs)(e.p,{children:["Now create file ",(0,o.jsx)(e.code,{children:"ApartsPie.vue"})," in the ",(0,o.jsx)(e.code,{children:"custom"})," folder of your project:"]}),"\n",(0,o.jsx)(e.pre,{children:(0,o.jsx)(e.code,{className:"language-html",metastring:'title="/custom/ApartsPie.vue"',children:'\n\n + diff --git a/blog/first-blog-post/index.html b/blog/first-blog-post/index.html index fe2e535ac..654a8967d 100644 --- a/blog/first-blog-post/index.html +++ b/blog/first-blog-post/index.html @@ -13,7 +13,7 @@ - + diff --git a/blog/index.html b/blog/index.html index 6f871355f..35a4c783f 100644 --- a/blog/index.html +++ b/blog/index.html @@ -13,7 +13,7 @@ - + diff --git a/blog/long-blog-post/index.html b/blog/long-blog-post/index.html index 2323bd922..ded8e81ec 100644 --- a/blog/long-blog-post/index.html +++ b/blog/long-blog-post/index.html @@ -13,7 +13,7 @@ - + diff --git a/blog/mdx-blog-post/index.html b/blog/mdx-blog-post/index.html index c851f460a..06e5544ec 100644 --- a/blog/mdx-blog-post/index.html +++ b/blog/mdx-blog-post/index.html @@ -13,7 +13,7 @@ - + diff --git a/blog/tags/docusaurus/index.html b/blog/tags/docusaurus/index.html index 21b0f7456..c0b1a9edc 100644 --- a/blog/tags/docusaurus/index.html +++ b/blog/tags/docusaurus/index.html @@ -13,7 +13,7 @@ - + diff --git a/blog/tags/facebook/index.html b/blog/tags/facebook/index.html index 5f1defc1c..dd27a0c52 100644 --- a/blog/tags/facebook/index.html +++ b/blog/tags/facebook/index.html @@ -13,7 +13,7 @@ - + diff --git a/blog/tags/hello/index.html b/blog/tags/hello/index.html index 3ae2126e1..08ffa0b92 100644 --- a/blog/tags/hello/index.html +++ b/blog/tags/hello/index.html @@ -13,7 +13,7 @@ - + diff --git a/blog/tags/hola/index.html b/blog/tags/hola/index.html index 11b5f8341..386936c5c 100644 --- a/blog/tags/hola/index.html +++ b/blog/tags/hola/index.html @@ -13,7 +13,7 @@ - + diff --git a/blog/tags/index.html b/blog/tags/index.html index 5841fe881..ac661cdcf 100644 --- a/blog/tags/index.html +++ b/blog/tags/index.html @@ -13,7 +13,7 @@ - + diff --git a/blog/welcome/index.html b/blog/welcome/index.html index 4ae163a18..2c38aa897 100644 --- a/blog/welcome/index.html +++ b/blog/welcome/index.html @@ -13,7 +13,7 @@ - + diff --git a/docs/api/index.html b/docs/api/index.html index ac09754b1..6ef3f8d75 100644 --- a/docs/api/index.html +++ b/docs/api/index.html @@ -13,7 +13,7 @@ - + diff --git a/docs/api/plugins/AuditLogPlugin/types/index.html b/docs/api/plugins/AuditLogPlugin/types/index.html index 31e771295..1d3380cae 100644 --- a/docs/api/plugins/AuditLogPlugin/types/index.html +++ b/docs/api/plugins/AuditLogPlugin/types/index.html @@ -13,7 +13,7 @@ - + diff --git a/docs/api/plugins/AuditLogPlugin/types/type-aliases/PluginOptions/index.html b/docs/api/plugins/AuditLogPlugin/types/type-aliases/PluginOptions/index.html index e61e0e837..7a6fa6c05 100644 --- a/docs/api/plugins/AuditLogPlugin/types/type-aliases/PluginOptions/index.html +++ b/docs/api/plugins/AuditLogPlugin/types/type-aliases/PluginOptions/index.html @@ -13,7 +13,7 @@ - + diff --git a/docs/api/plugins/ForeignInlineListPlugin/types/index.html b/docs/api/plugins/ForeignInlineListPlugin/types/index.html index 0516a354b..a2ffbabc3 100644 --- a/docs/api/plugins/ForeignInlineListPlugin/types/index.html +++ b/docs/api/plugins/ForeignInlineListPlugin/types/index.html @@ -13,7 +13,7 @@ - + diff --git a/docs/api/plugins/ForeignInlineListPlugin/types/type-aliases/PluginOptions/index.html b/docs/api/plugins/ForeignInlineListPlugin/types/type-aliases/PluginOptions/index.html index 7351a9600..72f36fe4a 100644 --- a/docs/api/plugins/ForeignInlineListPlugin/types/type-aliases/PluginOptions/index.html +++ b/docs/api/plugins/ForeignInlineListPlugin/types/type-aliases/PluginOptions/index.html @@ -13,7 +13,7 @@ - + diff --git a/docs/api/types/AdminForthConfig/enumerations/ActionCheckSource/index.html b/docs/api/types/AdminForthConfig/enumerations/ActionCheckSource/index.html index c2c9939b1..d4c01906d 100644 --- a/docs/api/types/AdminForthConfig/enumerations/ActionCheckSource/index.html +++ b/docs/api/types/AdminForthConfig/enumerations/ActionCheckSource/index.html @@ -13,7 +13,7 @@ - + diff --git a/docs/api/types/AdminForthConfig/enumerations/AdminForthDataTypes/index.html b/docs/api/types/AdminForthConfig/enumerations/AdminForthDataTypes/index.html index bdb5f1430..f50daacf4 100644 --- a/docs/api/types/AdminForthConfig/enumerations/AdminForthDataTypes/index.html +++ b/docs/api/types/AdminForthConfig/enumerations/AdminForthDataTypes/index.html @@ -13,7 +13,7 @@ - + diff --git a/docs/api/types/AdminForthConfig/enumerations/AdminForthFilterOperators/index.html b/docs/api/types/AdminForthConfig/enumerations/AdminForthFilterOperators/index.html index 990bcd4ae..26a86c22f 100644 --- a/docs/api/types/AdminForthConfig/enumerations/AdminForthFilterOperators/index.html +++ b/docs/api/types/AdminForthConfig/enumerations/AdminForthFilterOperators/index.html @@ -13,7 +13,7 @@ - + diff --git a/docs/api/types/AdminForthConfig/enumerations/AdminForthMenuTypes/index.html b/docs/api/types/AdminForthConfig/enumerations/AdminForthMenuTypes/index.html index bff96b4ca..247b4fe71 100644 --- a/docs/api/types/AdminForthConfig/enumerations/AdminForthMenuTypes/index.html +++ b/docs/api/types/AdminForthConfig/enumerations/AdminForthMenuTypes/index.html @@ -13,7 +13,7 @@ - + diff --git a/docs/api/types/AdminForthConfig/enumerations/AdminForthResourcePages/index.html b/docs/api/types/AdminForthConfig/enumerations/AdminForthResourcePages/index.html index fcdd86041..dca735907 100644 --- a/docs/api/types/AdminForthConfig/enumerations/AdminForthResourcePages/index.html +++ b/docs/api/types/AdminForthConfig/enumerations/AdminForthResourcePages/index.html @@ -13,7 +13,7 @@ - + diff --git a/docs/api/types/AdminForthConfig/enumerations/AdminForthSortDirections/index.html b/docs/api/types/AdminForthConfig/enumerations/AdminForthSortDirections/index.html index ce0f6c31b..27c40136f 100644 --- a/docs/api/types/AdminForthConfig/enumerations/AdminForthSortDirections/index.html +++ b/docs/api/types/AdminForthConfig/enumerations/AdminForthSortDirections/index.html @@ -13,7 +13,7 @@ - + diff --git a/docs/api/types/AdminForthConfig/enumerations/AllowedActionsEnum/index.html b/docs/api/types/AdminForthConfig/enumerations/AllowedActionsEnum/index.html index 11e769d6f..d3480223d 100644 --- a/docs/api/types/AdminForthConfig/enumerations/AllowedActionsEnum/index.html +++ b/docs/api/types/AdminForthConfig/enumerations/AllowedActionsEnum/index.html @@ -13,7 +13,7 @@ - + diff --git a/docs/api/types/AdminForthConfig/index.html b/docs/api/types/AdminForthConfig/index.html index f2035f976..7ba21e766 100644 --- a/docs/api/types/AdminForthConfig/index.html +++ b/docs/api/types/AdminForthConfig/index.html @@ -13,7 +13,7 @@ - + diff --git a/docs/api/types/AdminForthConfig/interfaces/AdminForthClass/index.html b/docs/api/types/AdminForthConfig/interfaces/AdminForthClass/index.html index 80f1a30de..213c6bc4c 100644 --- a/docs/api/types/AdminForthConfig/interfaces/AdminForthClass/index.html +++ b/docs/api/types/AdminForthConfig/interfaces/AdminForthClass/index.html @@ -13,7 +13,7 @@ - + diff --git a/docs/api/types/AdminForthConfig/interfaces/AdminForthDataSourceConnector/index.html b/docs/api/types/AdminForthConfig/interfaces/AdminForthDataSourceConnector/index.html index ef5719abe..06e547444 100644 --- a/docs/api/types/AdminForthConfig/interfaces/AdminForthDataSourceConnector/index.html +++ b/docs/api/types/AdminForthConfig/interfaces/AdminForthDataSourceConnector/index.html @@ -13,7 +13,7 @@ - + diff --git a/docs/api/types/AdminForthConfig/interfaces/AdminForthDataSourceConnectorConstructor/index.html b/docs/api/types/AdminForthConfig/interfaces/AdminForthDataSourceConnectorConstructor/index.html index 3a04d4140..3fcd6d5dc 100644 --- a/docs/api/types/AdminForthConfig/interfaces/AdminForthDataSourceConnectorConstructor/index.html +++ b/docs/api/types/AdminForthConfig/interfaces/AdminForthDataSourceConnectorConstructor/index.html @@ -13,7 +13,7 @@ - + diff --git a/docs/api/types/AdminForthConfig/interfaces/AdminForthPluginType/index.html b/docs/api/types/AdminForthConfig/interfaces/AdminForthPluginType/index.html index e8e227470..7d5249e26 100644 --- a/docs/api/types/AdminForthConfig/interfaces/AdminForthPluginType/index.html +++ b/docs/api/types/AdminForthConfig/interfaces/AdminForthPluginType/index.html @@ -13,7 +13,7 @@ - + diff --git a/docs/api/types/AdminForthConfig/interfaces/CodeInjectorType/index.html b/docs/api/types/AdminForthConfig/interfaces/CodeInjectorType/index.html index 5dc902906..a2e5e818d 100644 --- a/docs/api/types/AdminForthConfig/interfaces/CodeInjectorType/index.html +++ b/docs/api/types/AdminForthConfig/interfaces/CodeInjectorType/index.html @@ -13,7 +13,7 @@ - + diff --git a/docs/api/types/AdminForthConfig/interfaces/ExpressHttpServer/index.html b/docs/api/types/AdminForthConfig/interfaces/ExpressHttpServer/index.html index 37f49d77d..e8acfceda 100644 --- a/docs/api/types/AdminForthConfig/interfaces/ExpressHttpServer/index.html +++ b/docs/api/types/AdminForthConfig/interfaces/ExpressHttpServer/index.html @@ -13,7 +13,7 @@ - + diff --git a/docs/api/types/AdminForthConfig/interfaces/GenericHttpServer/index.html b/docs/api/types/AdminForthConfig/interfaces/GenericHttpServer/index.html index 0495c020f..1b13bd341 100644 --- a/docs/api/types/AdminForthConfig/interfaces/GenericHttpServer/index.html +++ b/docs/api/types/AdminForthConfig/interfaces/GenericHttpServer/index.html @@ -13,7 +13,7 @@ - + diff --git a/docs/api/types/AdminForthConfig/type-aliases/AdminForthColumnEnumItem/index.html b/docs/api/types/AdminForthConfig/type-aliases/AdminForthColumnEnumItem/index.html index f0b155008..834a86c03 100644 --- a/docs/api/types/AdminForthConfig/type-aliases/AdminForthColumnEnumItem/index.html +++ b/docs/api/types/AdminForthConfig/type-aliases/AdminForthColumnEnumItem/index.html @@ -13,7 +13,7 @@ - + diff --git a/docs/api/types/AdminForthConfig/type-aliases/AdminForthComponentDeclaration/index.html b/docs/api/types/AdminForthConfig/type-aliases/AdminForthComponentDeclaration/index.html index e62eb2e23..b2c4f17bc 100644 --- a/docs/api/types/AdminForthConfig/type-aliases/AdminForthComponentDeclaration/index.html +++ b/docs/api/types/AdminForthConfig/type-aliases/AdminForthComponentDeclaration/index.html @@ -13,7 +13,7 @@ - + diff --git a/docs/api/types/AdminForthConfig/type-aliases/AdminForthComponentDeclarationFull/index.html b/docs/api/types/AdminForthConfig/type-aliases/AdminForthComponentDeclarationFull/index.html index 75391a25b..01acaa76f 100644 --- a/docs/api/types/AdminForthConfig/type-aliases/AdminForthComponentDeclarationFull/index.html +++ b/docs/api/types/AdminForthConfig/type-aliases/AdminForthComponentDeclarationFull/index.html @@ -13,7 +13,7 @@ - + diff --git a/docs/api/types/AdminForthConfig/type-aliases/AdminForthConfig/index.html b/docs/api/types/AdminForthConfig/type-aliases/AdminForthConfig/index.html index 3af58082b..3cf8a2039 100644 --- a/docs/api/types/AdminForthConfig/type-aliases/AdminForthConfig/index.html +++ b/docs/api/types/AdminForthConfig/type-aliases/AdminForthConfig/index.html @@ -13,7 +13,7 @@ - + diff --git a/docs/api/types/AdminForthConfig/type-aliases/AdminForthConfigMenuItem/index.html b/docs/api/types/AdminForthConfig/type-aliases/AdminForthConfigMenuItem/index.html index f81290a07..ec27b1b28 100644 --- a/docs/api/types/AdminForthConfig/type-aliases/AdminForthConfigMenuItem/index.html +++ b/docs/api/types/AdminForthConfig/type-aliases/AdminForthConfigMenuItem/index.html @@ -13,7 +13,7 @@ - + diff --git a/docs/api/types/AdminForthConfig/type-aliases/AdminForthDataSource/index.html b/docs/api/types/AdminForthConfig/type-aliases/AdminForthDataSource/index.html index e7adc4422..bcb59c66a 100644 --- a/docs/api/types/AdminForthConfig/type-aliases/AdminForthDataSource/index.html +++ b/docs/api/types/AdminForthConfig/type-aliases/AdminForthDataSource/index.html @@ -13,7 +13,7 @@ - + diff --git a/docs/api/types/AdminForthConfig/type-aliases/AdminForthFieldComponents/index.html b/docs/api/types/AdminForthConfig/type-aliases/AdminForthFieldComponents/index.html index fbb47b65e..e198fbd8b 100644 --- a/docs/api/types/AdminForthConfig/type-aliases/AdminForthFieldComponents/index.html +++ b/docs/api/types/AdminForthConfig/type-aliases/AdminForthFieldComponents/index.html @@ -13,7 +13,7 @@ - + diff --git a/docs/api/types/AdminForthConfig/type-aliases/AdminForthForeignResource/index.html b/docs/api/types/AdminForthConfig/type-aliases/AdminForthForeignResource/index.html index 4b5e2f147..f89a4fdfa 100644 --- a/docs/api/types/AdminForthConfig/type-aliases/AdminForthForeignResource/index.html +++ b/docs/api/types/AdminForthConfig/type-aliases/AdminForthForeignResource/index.html @@ -13,7 +13,7 @@ - + diff --git a/docs/api/types/AdminForthConfig/type-aliases/AdminForthResource/index.html b/docs/api/types/AdminForthConfig/type-aliases/AdminForthResource/index.html index 6d2d4d31e..cf02196bc 100644 --- a/docs/api/types/AdminForthConfig/type-aliases/AdminForthResource/index.html +++ b/docs/api/types/AdminForthConfig/type-aliases/AdminForthResource/index.html @@ -13,7 +13,7 @@ - + diff --git a/docs/api/types/AdminForthConfig/type-aliases/AdminForthResourceColumn/index.html b/docs/api/types/AdminForthConfig/type-aliases/AdminForthResourceColumn/index.html index ab46bac92..68a2e2d4e 100644 --- a/docs/api/types/AdminForthConfig/type-aliases/AdminForthResourceColumn/index.html +++ b/docs/api/types/AdminForthConfig/type-aliases/AdminForthResourceColumn/index.html @@ -13,7 +13,7 @@ - + diff --git a/docs/api/types/AdminForthConfig/type-aliases/AdminUser/index.html b/docs/api/types/AdminForthConfig/type-aliases/AdminUser/index.html index 5eb56abf2..b836d76fe 100644 --- a/docs/api/types/AdminForthConfig/type-aliases/AdminUser/index.html +++ b/docs/api/types/AdminForthConfig/type-aliases/AdminUser/index.html @@ -13,7 +13,7 @@ - + diff --git a/docs/api/types/AdminForthConfig/type-aliases/AfterDataSourceResponseFunction/index.html b/docs/api/types/AdminForthConfig/type-aliases/AfterDataSourceResponseFunction/index.html index b85fc106e..d448012d6 100644 --- a/docs/api/types/AdminForthConfig/type-aliases/AfterDataSourceResponseFunction/index.html +++ b/docs/api/types/AdminForthConfig/type-aliases/AfterDataSourceResponseFunction/index.html @@ -13,7 +13,7 @@ - + diff --git a/docs/api/types/AdminForthConfig/type-aliases/AfterSaveFunction/index.html b/docs/api/types/AdminForthConfig/type-aliases/AfterSaveFunction/index.html index b5ed936e5..70d6fa7c6 100644 --- a/docs/api/types/AdminForthConfig/type-aliases/AfterSaveFunction/index.html +++ b/docs/api/types/AdminForthConfig/type-aliases/AfterSaveFunction/index.html @@ -13,7 +13,7 @@ - + diff --git a/docs/api/types/AdminForthConfig/type-aliases/AllowedActionValue/index.html b/docs/api/types/AdminForthConfig/type-aliases/AllowedActionValue/index.html index 434fdbb2c..91951d32b 100644 --- a/docs/api/types/AdminForthConfig/type-aliases/AllowedActionValue/index.html +++ b/docs/api/types/AdminForthConfig/type-aliases/AllowedActionValue/index.html @@ -13,7 +13,7 @@ - + diff --git a/docs/api/types/AdminForthConfig/type-aliases/AllowedActions/index.html b/docs/api/types/AdminForthConfig/type-aliases/AllowedActions/index.html index c9f10fcfc..cad7db959 100644 --- a/docs/api/types/AdminForthConfig/type-aliases/AllowedActions/index.html +++ b/docs/api/types/AdminForthConfig/type-aliases/AllowedActions/index.html @@ -13,7 +13,7 @@ - + diff --git a/docs/api/types/AdminForthConfig/type-aliases/BeforeDataSourceRequestFunction/index.html b/docs/api/types/AdminForthConfig/type-aliases/BeforeDataSourceRequestFunction/index.html index 9dab329cd..d3df1b6ff 100644 --- a/docs/api/types/AdminForthConfig/type-aliases/BeforeDataSourceRequestFunction/index.html +++ b/docs/api/types/AdminForthConfig/type-aliases/BeforeDataSourceRequestFunction/index.html @@ -13,7 +13,7 @@ - + diff --git a/docs/api/types/AdminForthConfig/type-aliases/BeforeSaveFunction/index.html b/docs/api/types/AdminForthConfig/type-aliases/BeforeSaveFunction/index.html index 3a420a119..a13804263 100644 --- a/docs/api/types/AdminForthConfig/type-aliases/BeforeSaveFunction/index.html +++ b/docs/api/types/AdminForthConfig/type-aliases/BeforeSaveFunction/index.html @@ -13,7 +13,7 @@ - + diff --git a/docs/api/types/AdminForthConfig/type-aliases/ValidationObject/index.html b/docs/api/types/AdminForthConfig/type-aliases/ValidationObject/index.html index 94a3de2e5..4bf0019cf 100644 --- a/docs/api/types/AdminForthConfig/type-aliases/ValidationObject/index.html +++ b/docs/api/types/AdminForthConfig/type-aliases/ValidationObject/index.html @@ -13,7 +13,7 @@ - + diff --git a/docs/api/types/FrontendAPI/enumerations/AlertVariant/index.html b/docs/api/types/FrontendAPI/enumerations/AlertVariant/index.html index 635fc7468..a87948049 100644 --- a/docs/api/types/FrontendAPI/enumerations/AlertVariant/index.html +++ b/docs/api/types/FrontendAPI/enumerations/AlertVariant/index.html @@ -13,7 +13,7 @@ - + diff --git a/docs/api/types/FrontendAPI/index.html b/docs/api/types/FrontendAPI/index.html index 3e3103de8..0f57c884c 100644 --- a/docs/api/types/FrontendAPI/index.html +++ b/docs/api/types/FrontendAPI/index.html @@ -13,7 +13,7 @@ - + diff --git a/docs/api/types/FrontendAPI/interfaces/FrontendAPIInterface/index.html b/docs/api/types/FrontendAPI/interfaces/FrontendAPIInterface/index.html index 866c79349..ede55d994 100644 --- a/docs/api/types/FrontendAPI/interfaces/FrontendAPIInterface/index.html +++ b/docs/api/types/FrontendAPI/interfaces/FrontendAPIInterface/index.html @@ -13,7 +13,7 @@ - + diff --git a/docs/api/types/FrontendAPI/type-aliases/AlertParams/index.html b/docs/api/types/FrontendAPI/type-aliases/AlertParams/index.html index 03e3f853d..32c74b9d5 100644 --- a/docs/api/types/FrontendAPI/type-aliases/AlertParams/index.html +++ b/docs/api/types/FrontendAPI/type-aliases/AlertParams/index.html @@ -13,7 +13,7 @@ - + diff --git a/docs/api/types/FrontendAPI/type-aliases/ConfirmParams/index.html b/docs/api/types/FrontendAPI/type-aliases/ConfirmParams/index.html index b0a0b0a81..47a202a0e 100644 --- a/docs/api/types/FrontendAPI/type-aliases/ConfirmParams/index.html +++ b/docs/api/types/FrontendAPI/type-aliases/ConfirmParams/index.html @@ -13,7 +13,7 @@ - + diff --git a/docs/tutorial/Customization/alert/index.html b/docs/tutorial/Customization/alert/index.html index de9a62e1c..cf4884bcd 100644 --- a/docs/tutorial/Customization/alert/index.html +++ b/docs/tutorial/Customization/alert/index.html @@ -13,7 +13,7 @@ - + diff --git a/docs/tutorial/Customization/branding/index.html b/docs/tutorial/Customization/branding/index.html index 1658166a4..6db764a37 100644 --- a/docs/tutorial/Customization/branding/index.html +++ b/docs/tutorial/Customization/branding/index.html @@ -13,7 +13,7 @@ - + diff --git a/docs/tutorial/Customization/bulkActions/index.html b/docs/tutorial/Customization/bulkActions/index.html index 813d07677..97aecd46a 100644 --- a/docs/tutorial/Customization/bulkActions/index.html +++ b/docs/tutorial/Customization/bulkActions/index.html @@ -13,7 +13,7 @@ - + diff --git a/docs/tutorial/Customization/customFieldRendering/index.html b/docs/tutorial/Customization/customFieldRendering/index.html index 220aed823..b104c87f7 100644 --- a/docs/tutorial/Customization/customFieldRendering/index.html +++ b/docs/tutorial/Customization/customFieldRendering/index.html @@ -13,7 +13,7 @@ - + diff --git a/docs/tutorial/Customization/customPages/index.html b/docs/tutorial/Customization/customPages/index.html index dc68c139a..fa006cd1c 100644 --- a/docs/tutorial/Customization/customPages/index.html +++ b/docs/tutorial/Customization/customPages/index.html @@ -13,7 +13,7 @@ - + @@ -23,7 +23,7 @@

Install CHart.js library into your main package (near index.ts):

npm install apexcharts --save

Create a Vue component in the custom directory of your project, e.g. Dashboard.vue:

-
/custom/Dashboard.vue
<template>
<div class="px-4 py-8 bg-blue-50 dark:bg-gray-900 dark:shadow-none h-screen">
<h1 class="mb-4 text-xl font-extrabold text-gray-900 dark:text-white md:text-2xl lg:text-3xl"><span
class="text-transparent bg-clip-text bg-gradient-to-r to-emerald-600 from-sky-400">Appartments</span>
Statistics.</h1>

<div class="grid grid-cols-3 gap-4">
<div class="max-w-md w-full bg-white rounded-lg shadow dark:bg-gray-800 p-4 md:p-6" v-if="data">
<div class="flex justify-between">
<div>
<h5 class="leading-none text-3xl font-bold text-gray-900 dark:text-white pb-2">{{ data.totalAparts }}</h5>
<p class="text-base font-normal text-gray-500 dark:text-gray-400">Apartments last 7 days</p>
</div>

</div>
<div id="area-chart"></div>

</div>

<div class="w-full bg-white rounded-lg shadow dark:bg-gray-800 p-4 md:p-6 row-span-2 col-span-2" v-if="data">

<div class="grid grid-cols-2 py-3">
<dl>
<dt class="text-base font-normal text-gray-500 dark:text-gray-400 pb-1">Listed price</dt>
<dd class="leading-none text-xl font-bold text-green-500 dark:text-green-400">{{
new Intl.NumberFormat('en-US', { style: 'currency', currency: 'USD' }).format(
data.totalListedPrice,
) }}
</dd>
</dl>
<dl>
<dt class="text-base font-normal text-gray-500 dark:text-gray-400 pb-1">Unlisted price</dt>
<dd class="leading-none text-xl font-bold text-red-600 dark:text-red-500">{{
new Intl.NumberFormat('en-US', { style: 'currency', currency: 'USD' }).format(
data.totalUnlistedPrice,
) }}
</dd>
</dl>
</div>

<div id="bar-chart"></div>

</div>

<div class="max-w-md w-full bg-white rounded-lg shadow dark:bg-gray-800 p-4 md:p-6" v-if="data">
<div class="flex justify-between mb-5">
<div>
<p class="text-base font-normal text-gray-500 dark:text-gray-400">
Unlisted vs Listed price
</p>
</div>
</div>
<div id="size-chart" class="[&>div]:mx-auto"></div>
</div>
</div>

</div>
</template>

<script setup>
import { ref, onMounted } from 'vue';
import ApexCharts from 'apexcharts';
import dayjs from 'dayjs';

const data = ref({});

const optionsC1 = {
chart: {
height: 145,
type: "area",
fontFamily: "Inter, sans-serif",
dropShadow: {
enabled: false,
},
toolbar: {
show: false,
},
},
tooltip: {
enabled: true,
x: {
show: false,
},
},
fill: {
type: "gradient",
gradient: {
opacityFrom: 0.55,
opacityTo: 0,
shade: "#1C64F2",
gradientToColors: ["#1C64F2"],
},
},
dataLabels: {
enabled: false,
},
stroke: {
width: 6,
},
grid: {
show: false,
strokeDashArray: 4,
padding: {
left: 2,
right: 2,
top: 0
},
},
series: [
{
name: "Added appartments",
data: [],
color: "#1A56DB",
},
],
xaxis: {
categories: [],
labels: {
show: false,
},
axisBorder: {
show: false,
},
axisTicks: {
show: false,
},
},
yaxis: {
show: false,
},
};

const optionsC2 = {
series: [
{
name: "Listed",
color: "#31C48D",
data: [],
},
{
name: "Unlisted",
data: [],
color: "#F05252",
}
],
chart: {
sparkline: {
enabled: false,
},
type: "bar",
width: "100%",
height: 400,
toolbar: {
show: false,
}
},
fill: {
opacity: 1,
},
plotOptions: {
bar: {
horizontal: true,
columnWidth: "100%",
borderRadiusApplication: "end",
borderRadius: 6,
dataLabels: {
position: "top",
},
},
},
legend: {
show: true,
position: "bottom",
},
dataLabels: {
enabled: false,
},
tooltip: {
shared: true,
intersect: false,
formatter: function (value) {
return value
},
},
xaxis: {
labels: {
show: true,
style: {
fontFamily: "Inter, sans-serif",
cssClass: 'text-xs font-normal fill-gray-500 dark:fill-gray-400'
},
formatter: function (value) {
return value
}
},
categories: [],
axisTicks: {
show: false,
},
axisBorder: {
show: false,
},
},
yaxis: {
labels: {
show: true,
style: {
fontFamily: "Inter, sans-serif",
cssClass: 'text-xs font-normal fill-gray-500 dark:fill-gray-400'
}
}
},
grid: {
show: true,
strokeDashArray: 4,
padding: {
left: 10,
right: 2,
// top: -20
},
},
fill: {
opacity: 1,
}
}

const optionsC3 = {
chart: {
height: 145,
type: "area",
fontFamily: "Inter, sans-serif",
dropShadow: {
enabled: false,
},
toolbar: {
show: false,
},
},
tooltip: {
enabled: true,
x: {
show: false,
},
},
fill: {
type: "gradient",
gradient: {
opacityFrom: 0.55,
opacityTo: 0,
shade: "#1C64F2",
gradientToColors: ["#1C64F2"],
},
},
dataLabels: {
enabled: false,
},
stroke: {
width: 6,
},
grid: {
show: false,
strokeDashArray: 4,
padding: {
left: 2,
right: 2,
top: -26
},
},
series: [
{
name: "Listed Price",
data: [],
color: "#1A56DB",
},
{
name: "Unlisted Price",
data: [],
color: "#7E3BF2",
},
],
xaxis: {
categories: [],
labels: {
show: false,
},
axisBorder: {
show: false,
},
axisTicks: {
show: false,
},
},
yaxis: {
show: false,
labels: {
formatter: function (value) {
return '$' + value;
}
}
},
}


onMounted(async () => {
// Fetch data from the API
// and set it to the chartData
try {
const resp = await fetch('/api/dashboard/');
if (resp.status === 401) {
// user will be redirected to login page automatically so no need to handle anything here
return;
}
data.value = await resp.json();
} catch (error) {
window.adminforth.alert({
message: `Error fetching data: ${error.message}`,
variant: 'danger',
timeout: 'unlimited'
});
}

const apartsByDaysReverse = data.value.apartsByDays.reverse();

optionsC1.series[0].data = apartsByDaysReverse.map((item) => item.count);
optionsC1.xaxis.categories = apartsByDaysReverse.map((item) => dayjs(item.day).format('DD MMM'));
const chart = new ApexCharts(document.getElementById("area-chart"), optionsC1);
chart.render();

optionsC2.series[0].data = data.value.listedVsUnlistedByDays.map((item) => item.listed);
optionsC2.series[1].data = data.value.listedVsUnlistedByDays.map((item) => item.unlisted);
optionsC2.xaxis.categories = data.value.listedVsUnlistedByDays.map((item) => dayjs(item.day).format('DD MMM'));
const chart2 = new ApexCharts(document.getElementById("bar-chart"), optionsC2);
chart2.render();

optionsC3.series[0].data = data.value.listedVsUnlistedPriceByDays.map((item) => item.listedPrice.toFixed(2));
optionsC3.series[1].data = data.value.listedVsUnlistedPriceByDays.map((item) => item.unlistedPrice.toFixed(2));
optionsC3.xaxis.categories = data.value.listedVsUnlistedPriceByDays.map((item) => dayjs(item.day).format('DD MMM'));
const chart3 = new ApexCharts(document.getElementById("size-chart"), optionsC3);
chart3.render();

})

</script>
+
./custom/Dashboard.vue
<template>
<div class="px-4 py-8 bg-blue-50 dark:bg-gray-900 dark:shadow-none h-screen">
<h1 class="mb-4 text-xl font-extrabold text-gray-900 dark:text-white md:text-2xl lg:text-3xl"><span
class="text-transparent bg-clip-text bg-gradient-to-r to-emerald-600 from-sky-400">Appartments</span>
Statistics.</h1>

<div class="grid grid-cols-3 gap-4">
<div class="max-w-md w-full bg-white rounded-lg shadow dark:bg-gray-800 p-4 md:p-6" v-if="data">
<div class="flex justify-between">
<div>
<h5 class="leading-none text-3xl font-bold text-gray-900 dark:text-white pb-2">{{ data.totalAparts }}</h5>
<p class="text-base font-normal text-gray-500 dark:text-gray-400">Apartments last 7 days</p>
</div>

</div>
<div id="area-chart"></div>

</div>

<div class="w-full bg-white rounded-lg shadow dark:bg-gray-800 p-4 md:p-6 row-span-2 col-span-2" v-if="data">

<div class="grid grid-cols-2 py-3">
<dl>
<dt class="text-base font-normal text-gray-500 dark:text-gray-400 pb-1">Listed price</dt>
<dd class="leading-none text-xl font-bold text-green-500 dark:text-green-400">{{
new Intl.NumberFormat('en-US', { style: 'currency', currency: 'USD' }).format(
data.totalListedPrice,
) }}
</dd>
</dl>
<dl>
<dt class="text-base font-normal text-gray-500 dark:text-gray-400 pb-1">Unlisted price</dt>
<dd class="leading-none text-xl font-bold text-red-600 dark:text-red-500">{{
new Intl.NumberFormat('en-US', { style: 'currency', currency: 'USD' }).format(
data.totalUnlistedPrice,
) }}
</dd>
</dl>
</div>

<div id="bar-chart"></div>

</div>

<div class="max-w-md w-full bg-white rounded-lg shadow dark:bg-gray-800 p-4 md:p-6" v-if="data">
<div class="flex justify-between mb-5">
<div>
<p class="text-base font-normal text-gray-500 dark:text-gray-400">
Unlisted vs Listed price
</p>
</div>
</div>
<div id="size-chart" class="[&>div]:mx-auto"></div>
</div>
</div>

</div>
</template>

<script setup>
import { ref, onMounted } from 'vue';
import ApexCharts from 'apexcharts';
import dayjs from 'dayjs';

const data = ref({});

const optionsC1 = {
chart: {
height: 145,
type: "area",
fontFamily: "Inter, sans-serif",
dropShadow: {
enabled: false,
},
toolbar: {
show: false,
},
},
tooltip: {
enabled: true,
x: {
show: false,
},
},
fill: {
type: "gradient",
gradient: {
opacityFrom: 0.55,
opacityTo: 0,
shade: "#1C64F2",
gradientToColors: ["#1C64F2"],
},
},
dataLabels: {
enabled: false,
},
stroke: {
width: 6,
},
grid: {
show: false,
strokeDashArray: 4,
padding: {
left: 2,
right: 2,
top: 0
},
},
series: [
{
name: "Added appartments",
data: [],
color: "#1A56DB",
},
],
xaxis: {
categories: [],
labels: {
show: false,
},
axisBorder: {
show: false,
},
axisTicks: {
show: false,
},
},
yaxis: {
show: false,
},
};

const optionsC2 = {
series: [
{
name: "Listed",
color: "#31C48D",
data: [],
},
{
name: "Unlisted",
data: [],
color: "#F05252",
}
],
chart: {
sparkline: {
enabled: false,
},
type: "bar",
width: "100%",
height: 400,
toolbar: {
show: false,
}
},
fill: {
opacity: 1,
},
plotOptions: {
bar: {
horizontal: true,
columnWidth: "100%",
borderRadiusApplication: "end",
borderRadius: 6,
dataLabels: {
position: "top",
},
},
},
legend: {
show: true,
position: "bottom",
},
dataLabels: {
enabled: false,
},
tooltip: {
shared: true,
intersect: false,
formatter: function (value) {
return value
},
},
xaxis: {
labels: {
show: true,
style: {
fontFamily: "Inter, sans-serif",
cssClass: 'text-xs font-normal fill-gray-500 dark:fill-gray-400'
},
formatter: function (value) {
return value
}
},
categories: [],
axisTicks: {
show: false,
},
axisBorder: {
show: false,
},
},
yaxis: {
labels: {
show: true,
style: {
fontFamily: "Inter, sans-serif",
cssClass: 'text-xs font-normal fill-gray-500 dark:fill-gray-400'
}
}
},
grid: {
show: true,
strokeDashArray: 4,
padding: {
left: 10,
right: 2,
// top: -20
},
},
fill: {
opacity: 1,
}
}

const optionsC3 = {
chart: {
height: 145,
type: "area",
fontFamily: "Inter, sans-serif",
dropShadow: {
enabled: false,
},
toolbar: {
show: false,
},
},
tooltip: {
enabled: true,
x: {
show: false,
},
},
fill: {
type: "gradient",
gradient: {
opacityFrom: 0.55,
opacityTo: 0,
shade: "#1C64F2",
gradientToColors: ["#1C64F2"],
},
},
dataLabels: {
enabled: false,
},
stroke: {
width: 6,
},
grid: {
show: false,
strokeDashArray: 4,
padding: {
left: 2,
right: 2,
top: -26
},
},
series: [
{
name: "Listed Price",
data: [],
color: "#1A56DB",
},
{
name: "Unlisted Price",
data: [],
color: "#7E3BF2",
},
],
xaxis: {
categories: [],
labels: {
show: false,
},
axisBorder: {
show: false,
},
axisTicks: {
show: false,
},
},
yaxis: {
show: false,
labels: {
formatter: function (value) {
return '$' + value;
}
}
},
}

onMounted(async () => {
// Fetch data from the API
// and set it to the chartData
try {
const resp = await fetch('/api/dashboard/');
if (resp.status === 401) {
// user will be redirected to login page automatically so no need to handle anything here
return;
}
data.value = await resp.json();
} catch (error) {
window.adminforth.alert({
message: `Error fetching data: ${error.message}`,
variant: 'danger',
timeout: 'unlimited'
});
}

const apartsByDaysReverse = data.value.apartsByDays.reverse();

optionsC1.series[0].data = apartsByDaysReverse.map((item) => item.count);
optionsC1.xaxis.categories = apartsByDaysReverse.map((item) => dayjs(item.day).format('DD MMM'));
const chart = new ApexCharts(document.getElementById("area-chart"), optionsC1);
chart.render();

optionsC2.series[0].data = data.value.listedVsUnlistedByDays.map((item) => item.listed);
optionsC2.series[1].data = data.value.listedVsUnlistedByDays.map((item) => item.unlisted);
optionsC2.xaxis.categories = data.value.listedVsUnlistedByDays.map((item) => dayjs(item.day).format('DD MMM'));
const chart2 = new ApexCharts(document.getElementById("bar-chart"), optionsC2);
chart2.render();

optionsC3.series[0].data = data.value.listedVsUnlistedPriceByDays.map((item) => item.listedPrice.toFixed(2));
optionsC3.series[1].data = data.value.listedVsUnlistedPriceByDays.map((item) => item.unlistedPrice.toFixed(2));
optionsC3.xaxis.categories = data.value.listedVsUnlistedPriceByDays.map((item) => dayjs(item.day).format('DD MMM'));
const chart3 = new ApexCharts(document.getElementById("size-chart"), optionsC3);
chart3.render();
})
</script>

🫨 use https://flowbite.com/ to copy-paste pre-designed tailwind design blocks for your pages

diff --git a/docs/tutorial/Customization/hooks/index.html b/docs/tutorial/Customization/hooks/index.html index 97aba2f04..ccf048a75 100644 --- a/docs/tutorial/Customization/hooks/index.html +++ b/docs/tutorial/Customization/hooks/index.html @@ -13,7 +13,7 @@ - + @@ -28,6 +28,6 @@

Modify the data before it is saved to the database​

Let's add reference to adminUser when user creates a new apartment:

-
./index.ts
import type { AdminUser } from  'adminforth/types/AdminForthConfig.js';

{
...
resourceId: 'aparts',
columns: [
...
{
name: 'realtor_id',
...
+ showIn: ['list', 'show', 'edit'], // don't even show this field in create
...
},
...
],
...
+ hooks: {
+ create: {
+ beforeSave: async ({ adminUser, record }: { adminUser: AdminUser, record: any }) => {
+ if (adminUser.isRoot) {
+ return { ok: false, error: "Root user can't create appartment, relogin as DB user" };
+ }
+ record.realtor_id = adminUser.dbUser.id;
+ return { ok: true, record };
+ }
+ }
+ }
}
+
./index.ts
import type { AdminUser } from  'adminforth/types/AdminForthConfig.js';

{
...
resourceId: 'aparts',
columns: [
...
{
name: 'realtor_id',
...
showIn: ['list', 'show', 'edit'], // don't even show this field in create
...
},
...
],
...
hooks: {
create: {
beforeSave: async ({ adminUser, record }: { adminUser: AdminUser, record: any }) => {
if (adminUser.isRoot) {
return { ok: false, error: "Root user can't create appartment, relogin as DB user" };
}
record.realtor_id = adminUser.dbUser.id;
return { ok: true, record };
}
}
}
}
\ No newline at end of file diff --git a/docs/tutorial/Customization/limitingAccess/index.html b/docs/tutorial/Customization/limitingAccess/index.html index a3a00b22d..0cd68be7b 100644 --- a/docs/tutorial/Customization/limitingAccess/index.html +++ b/docs/tutorial/Customization/limitingAccess/index.html @@ -13,7 +13,7 @@ - + @@ -21,10 +21,10 @@

Statically disable some action​

You can use options.allowedActions on resource to limit access to the resource actions (list, show, create, edit, delete).

If you want to disable deletion of the resource records for all users:

-
{
...
resourceId: 'users',
...
options: {
allowedActions: {
delete: false
}
...
}
}
+
./index.ts
{
...
resourceId: 'users',
...
options: {
allowedActions: {
delete: false
}
}
}

Disable some action based on logged in user record or role​

If you want to disable deletion of apartments for all users apart from users with role superadmin:

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

{
...
resourceId: 'aparts',
...
options: {
allowedActions: {
delete: async ({ adminUser }: { adminUser: AdminUser }): Promise<boolean> => {
// important: if adminUser.isRoot, the adminUser.dbUser is undefined
return adminUser.isRoot || adminUser.dbUser.role === 'superadmin';
}
}
...
}
}
+
import type { AdminUser } from  'adminforth/types/AdminForthConfig.js';

{
...
resourceId: 'aparts',
...
options: {
allowedActions: {
delete: async ({ adminUser }: { adminUser: AdminUser }): Promise<boolean> => {
// important: if adminUser.isRoot, then adminUser.dbUser is undefined
return adminUser.isRoot || adminUser.dbUser.role === 'superadmin';
}
}
...
}
}

🫨 instead of reading role from user you can check permission using complex ACL/RBAC models with permissions stored in the database. However we recommend you to keep in mind that allowedActions callback is called on every request related to resource, so it should be fast. @@ -32,10 +32,10 @@

Reuse the same callback for multiple actions​

Let's disable creating and editing of new users for all users apart from users with role superadmin, and at the same time disable deletion for all users:

-

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

async function canModifyUsers({ adminUser }: { adminUser: AdminUser }): boolean {
// important: if adminUser.isRoot, the adminUser.dbUser is undefined
return adminUser.isRoot || adminUser.dbUser.role === 'superadmin';
}

{
...
resourceId: 'users',
...
options: {
allowedActions: {
create: canModifyUsers,
edit: canModifyUsers,
delete: false
}
...
}
}
+
./index.ts
import type { AdminUser } from  'adminforth/types/AdminForthConfig.js';

async function canModifyUsers({ adminUser }: { adminUser: AdminUser }): boolean {
// important: if adminUser.isRoot, the adminUser.dbUser is undefined
return adminUser.isRoot || adminUser.dbUser.role === 'superadmin';
}

{
...
resourceId: 'users',
...
options: {
allowedActions: {
create: canModifyUsers,
edit: canModifyUsers,
delete: false
}
...
}
}

Customizing the access control based on resource values​

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

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


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


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


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


{
...
resourceId: 'aparts',
...
options: {
allowedActions: {
edit: canModifyAppart,
}
...
}
}
\ No newline at end of file diff --git a/docs/tutorial/Customization/pageInjections/index.html b/docs/tutorial/Customization/pageInjections/index.html index 4ac5f96b3..38b274077 100644 --- a/docs/tutorial/Customization/pageInjections/index.html +++ b/docs/tutorial/Customization/pageInjections/index.html @@ -13,7 +13,7 @@ - + @@ -22,9 +22,9 @@

For example let's add a custom pie chart to the list page of the aparts resource. Pie chart will show the distribution of the rooms count and more over will allow to filter the list by the rooms count.

/index.ts
{
resourceId: 'aparts',
...
options: {
pageInjections: {
list: {
afterBreadcrumbs: '@@/ApartsPie.vue',
}
}
}
}

Now create file ApartsPie.vue in the custom folder of your project:

-
/custom/ApartsPie.vue
<template>
<div class="max-w-sm w-full bg-white rounded-lg shadow dark:bg-gray-800 p-4 md:p-4 mb-5">
<div id="pie-chart"></div>
</div>
</template>

<script setup lang="ts">
import { onMounted, ref, Ref } from 'vue';
import ApexCharts from 'apexcharts';

const data: Ref<any[]> = ref([]);

const POSSIBLE_COLORS = ["#1C64F2", "#16BDCA", "#9061F9", "#F0A936", "#F55252", "#3B82F6", "#10B981", "#F472B6", "#6B7280"];

const chatOptions = {
series: [],
colors: POSSIBLE_COLORS,
chart: {
height: 200,
width: "100%",
type: "pie",
events: {
dataPointSelection: function (event, chartContext, config) {
if (config.selectedDataPoints[0].length) {
const selectedRoomsCount = data.value[config.dataPointIndex].rooms;
window.adminforth.updateListFilter({field: 'number_of_rooms', operator: 'eq', value: selectedRoomsCount});
} else {
// clear filter
window.adminforth.updateListFilter({field: 'number_of_rooms', value: undefined});
}
}
},
},

stroke: {
colors: ["white"],
lineCap: "",
},
plotOptions: {
pie: {
labels: {
show: true,
},
size: "100%",
dataLabels: {
offset: -25
}
},
},
labels: ["Direct", "Organic search", "Referrals"],
dataLabels: {
enabled: true,
style: {
fontFamily: "Inter, sans-serif",
},
},
legend: {
position: "right",
fontFamily: "Inter, sans-serif",
},
yaxis: {
labels: {
formatter: function (value) {
return value + "%"
},
},
},
xaxis: {
labels: {
formatter: function (value) {
return value + "%"
},
},
axisTicks: {
show: false,
},
axisBorder: {
show: false,
},
},
}


onMounted(async () => {
try {
const resp = await fetch('/api/aparts-by-room-percentages');
if (resp.status === 401) {
// user will be redirected to login page automatically so no need to handle anything here
return;
}
data.value = await resp.json();
} catch (error) {
window.adminforth.alert({
message: `Error fetching data: ${error.message}`,
variant: 'danger',
timeout: 'unlimited'
});
return;
}

chatOptions.series = data.value.map((item) => item.percentage);
chatOptions.labels = data.value.map((item) => `${item.rooms} rooms`);
const chart = new ApexCharts(document.getElementById("pie-chart"), chatOptions);
chart.render();

})

</script>
+
./custom/ApartsPie.vue
<template>
<div class="max-w-sm w-full bg-white rounded-lg shadow dark:bg-gray-800 p-4 md:p-4 mb-5">
<div id="pie-chart"></div>
</div>
</template>

<script setup lang="ts">
import { onMounted, ref, Ref } from 'vue';
import ApexCharts from 'apexcharts';

const data: Ref<any[]> = ref([]);

const POSSIBLE_COLORS = ["#1C64F2", "#16BDCA", "#9061F9", "#F0A936", "#F55252", "#3B82F6", "#10B981", "#F472B6", "#6B7280"];

const chatOptions = {
series: [],
colors: POSSIBLE_COLORS,
chart: {
height: 200,
width: "100%",
type: "pie",
events: {
dataPointSelection: function (event, chartContext, config) {
if (config.selectedDataPoints[0].length) {
const selectedRoomsCount = data.value[config.dataPointIndex].rooms;
window.adminforth.updateListFilter({field: 'number_of_rooms', operator: 'eq', value: selectedRoomsCount});
} else {
// clear filter
window.adminforth.updateListFilter({field: 'number_of_rooms', value: undefined});
}
}
},
},

stroke: {
colors: ["white"],
lineCap: "",
},
plotOptions: {
pie: {
labels: {
show: true,
},
size: "100%",
dataLabels: {
offset: -25
}
},
},
labels: ["Direct", "Organic search", "Referrals"],
dataLabels: {
enabled: true,
style: {
fontFamily: "Inter, sans-serif",
},
},
legend: {
position: "right",
fontFamily: "Inter, sans-serif",
},
yaxis: {
labels: {
formatter: function (value) {
return value + "%"
},
},
},
xaxis: {
labels: {
formatter: function (value) {
return value + "%"
},
},
axisTicks: {
show: false,
},
axisBorder: {
show: false,
},
},
}

onMounted(async () => {
try {
const resp = await fetch('/api/aparts-by-room-percentages');
if (resp.status === 401) {
// user will be redirected to login page automatically so no need to handle anything here
return;
}
data.value = await resp.json();
} catch (error) {
window.adminforth.alert({
message: `Error fetching data: ${error.message}`,
variant: 'danger',
timeout: 'unlimited'
});
return;
}

chatOptions.series = data.value.map((item) => item.percentage);
chatOptions.labels = data.value.map((item) => `${item.rooms} rooms`);
const chart = new ApexCharts(document.getElementById("pie-chart"), chatOptions);
chart.render();

})

</script>

Also we have to add an Api to get percentages:

-
/index.ts
  app.get('/api/aparts-by-room-percentages/',
admin.express.authorize(
async (req, res) => {
const roomPercentages = await db.prepare(
`SELECT
number_of_rooms,
COUNT(*) as count
FROM apartments
GROUP BY number_of_rooms
ORDER BY number_of_rooms;
`
).all();

const totalAparts = roomPercentages.reduce((acc, { count }) => acc + count, 0);

res.json(
roomPercentages.map(
({ number_of_rooms, count }) => ({
rooms: number_of_rooms,
percentage: Math.round(count / totalAparts * 100),
})
)
);
}
)
);

// serve after you added all api
admin.discoverDatabases();
admin.express.serve(app)
+
./index.ts
  app.get('/api/aparts-by-room-percentages/',
admin.express.authorize(
async (req, res) => {
const roomPercentages = await db.prepare(
`SELECT
number_of_rooms,
COUNT(*) as count
FROM apartments
GROUP BY number_of_rooms
ORDER BY number_of_rooms;
`
).all();

const totalAparts = roomPercentages.reduce((acc, { count }) => acc + count, 0);

res.json(
roomPercentages.map(
({ number_of_rooms, count }) => ({
rooms: number_of_rooms,
percentage: Math.round(count / totalAparts * 100),
})
)
);
}
)
);

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

🫨 Please note that we are using window.adminforth.updateListFilter({field: 'number_of_rooms', operator: 'eq', value: selectedRoomsCount}); to set filter when we are located on apartments list page

diff --git a/docs/tutorial/Customization/virtualColumns/index.html b/docs/tutorial/Customization/virtualColumns/index.html index d766a88ad..8fa88cc3b 100644 --- a/docs/tutorial/Customization/virtualColumns/index.html +++ b/docs/tutorial/Customization/virtualColumns/index.html @@ -13,7 +13,7 @@ - + diff --git a/docs/tutorial/Plugins/AuditLog/index.html b/docs/tutorial/Plugins/AuditLog/index.html index af945cdca..dbed20555 100644 --- a/docs/tutorial/Plugins/AuditLog/index.html +++ b/docs/tutorial/Plugins/AuditLog/index.html @@ -13,7 +13,7 @@ - + @@ -26,8 +26,17 @@

Installation
import AuditLogPlugin from 'adminforth/dist/plugins/AuditLogPlugin/index.ts';

Getting Started will be used as base for this example.

Creating table for storing activity data​

-

For the first, to track records changes, we need to set up the database and table with certain fields inside.

+

For the first, to track records changes, we need to set up the database and table with certain fields inside where tracked data will be stored.

+

In this example, you should create this table in your own database:

CREATE TABLE audit_logs(
id uuid NOT NULL, -- identifier of applied change record
created_at timestamp without time zone, -- timestamp of applied change
resource_id varchar(255), -- identifier of resource where change were applied
user_id uuid, -- identifier of user who made the changes
"action" varchar(255), -- type of change (create, edit, delete)
diff text, -- delta betwen before/after versions
record_id varchar, -- identifier of record that been changed
PRIMARY KEY(id)
);
-

See API Reference for more all options.

+

Setting up the resource and dataSource for plugin​

+

Logger sets up for all the resources by default. But you can exclude unwanted resources with option "excludeResourceIds". In this example, we'll exclude resource "users" from logging.

+

Also, it excludes itself to avoid infinte logging loop.

+
    dataSources: [
...
{
id: 'db2',
url: '<url to your database>',
},
],
resources: [
...
{
dataSource: 'db2', table: 'audit_logs',
columns: [
{ name: 'id', primaryKey: true, required: false, fillOnCreate: ({initialRecord}: any) => uuid() },
{ name: 'created_at', required: false },
{ name: 'resource_id', required: false },
{ name: 'user_id', required: false },
{ name: 'action', required: false },
{ name: 'diff', required: false },
{ name: 'record_id', required: false },
],
options: {
allowedActions: {
edit: false,
delete: false,
}
},
plugins: [
new AuditLogPlugin({
excludeResourceIds: ['users'],
resourceColumns: {
resourceIdColumnName: 'resource_id',
resourceActionColumnName: 'action',
resourceDataColumnName: 'diff',
resourceUserIdColumnName: 'user_id',
resourceRecordIdColumnName: 'record_id',
resourceCreatedColumnName: 'created_at'
}
}),
],
}
]
+

Also, we need to add it to menu:

+
{
label: 'Logs',
icon: 'flowbite:search-outline',
resourceId: 'audit_logs',
}
+

That's it! Now you can see the logs in the table

+

alt text

+

See API Reference for more all options.

\ No newline at end of file diff --git a/docs/tutorial/Plugins/ForeignInlineList/index.html b/docs/tutorial/Plugins/ForeignInlineList/index.html index 82bdbfc83..7cc8fb0f6 100644 --- a/docs/tutorial/Plugins/ForeignInlineList/index.html +++ b/docs/tutorial/Plugins/ForeignInlineList/index.html @@ -13,7 +13,7 @@ - + diff --git a/docs/tutorial/deploy/index.html b/docs/tutorial/deploy/index.html index 988afc93d..e0e2b1a97 100644 --- a/docs/tutorial/deploy/index.html +++ b/docs/tutorial/deploy/index.html @@ -13,7 +13,7 @@ - + diff --git a/docs/tutorial/gettingStarted/index.html b/docs/tutorial/gettingStarted/index.html index b19447bc9..7b9d70390 100644 --- a/docs/tutorial/gettingStarted/index.html +++ b/docs/tutorial/gettingStarted/index.html @@ -13,7 +13,7 @@ - + diff --git a/docs/tutorial/glossary/index.html b/docs/tutorial/glossary/index.html index 6478c91a2..e98182c0e 100644 --- a/docs/tutorial/glossary/index.html +++ b/docs/tutorial/glossary/index.html @@ -13,7 +13,7 @@ - + diff --git a/index.html b/index.html index f32af48c5..81a47aa09 100644 --- a/index.html +++ b/index.html @@ -13,7 +13,7 @@ - + diff --git a/markdown-page/index.html b/markdown-page/index.html index a650b5233..af75a0a76 100644 --- a/markdown-page/index.html +++ b/markdown-page/index.html @@ -13,7 +13,7 @@ - + diff --git a/search/index.html b/search/index.html index bd7b5eea0..c57a2e777 100644 --- a/search/index.html +++ b/search/index.html @@ -13,7 +13,7 @@ - +