Skip to content

Commit c89f9dc

Browse files
committed
Merge branch 'next' of github.com:devforth/adminforth into next
2 parents 991ee87 + 0578a18 commit c89f9dc

38 files changed

+1081
-658
lines changed

adminforth/commands/createApp/templates/index.ts.hbs

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -30,7 +30,8 @@ export const admin = new AdminForth({
3030
brandLogo: '@@/assets/logo.svg',
3131
datesFormat: 'DD MMM',
3232
timeFormat: 'HH:mm a',
33-
showBrandNameInSidebar: true,
33+
showBrandNameInSidebar: true,
34+
showBrandLogoInSidebar: true,
3435
emptyFieldPlaceholder: '-',
3536
styles: {
3637
colors: {

adminforth/documentation/docs/tutorial/03-Customization/01-branding.md

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -54,6 +54,18 @@ showBrandNameInSidebar: false,
5454

5555
`brandName` will still be used in the other places e.g. login form.
5656

57+
## Removing brand logo from sidebar
58+
59+
If you want to hide the logo image in the sidebar while keeping the brand name text (or vice versa), disable the logo with:
60+
61+
```ts title='./index.ts'
62+
brandName: 'My App',
63+
//diff-add
64+
showBrandLogoInSidebar: false,
65+
```
66+
67+
By default, the logo is shown when available. This flag controls the sidebar logo only and does not affect the login page.
68+
5769
## Theming
5870

5971
AdminForth uses TailwindCSS for styling. You are able to customize the look of the application by changing the TailwindCSS configuration.

adminforth/documentation/docs/tutorial/03-Customization/08-pageInjections.md

Lines changed: 21 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -426,6 +426,7 @@ Also there are:
426426
427427
* `config.customization.globalInjections.header`
428428
* `config.customization.globalInjections.sidebar`
429+
* `config.customization.globalInjections.sidebarTop` — renders inline at the very top of the sidebar, on the same row with the logo/brand name. If the logo is hidden via `showBrandLogoInSidebar: false`, this area expands to the whole row width.
429430
* `config.customization.globalInjections.everyPageBottom`
430431
431432
Unlike `userMenu`, `header` and `sidebar` injections, `everyPageBottom` will be added to the bottom of every page even when user is not logged in.
@@ -469,4 +470,23 @@ onMounted(() => {
469470
});
470471
});
471472
</script>
472-
```
473+
```
474+
475+
## Sidebar Top Injection
476+
477+
You can place compact controls on the very top line of the sidebar, next to the logo/brand name:
478+
479+
```ts title="/index.ts"
480+
new AdminForth({
481+
...
482+
customization: {
483+
globalInjections: {
484+
sidebarTop: [
485+
'@@/QuickSwitch.vue',
486+
],
487+
}
488+
}
489+
})
490+
```
491+
492+
If you hide the logo with `showBrandLogoInSidebar: false`, components injected via `sidebarTop` will take the whole line width.

adminforth/documentation/docs/tutorial/03-Customization/15-afcl.md

Lines changed: 31 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -797,7 +797,7 @@ const isoFlagToEmoji = (iso) => iso.toUpperCase().replace(/./g, char => String.f
797797

798798

799799

800-
## Pagination
800+
### Pagination
801801

802802
Table provides front-end side pagination. You can set `pageSize` (default is 10) to set how many rows to show per page.
803803
If there is less then `pageSize` rows, pagination will not be shown.
@@ -829,6 +829,36 @@ If there is less then `pageSize` rows, pagination will not be shown.
829829
</div>
830830
</div>
831831

832+
### Server-side pagination
833+
834+
To load pages dynamically, simply pass async callback to data:
835+
836+
```ts
837+
async function loadPageData(offset, limit) {
838+
// in real app do await callAdminForthApi or await fetch to get date, use offset and limit value to slice data
839+
return {
840+
data: [
841+
{ name: 'John', age: offset, country: 'US' },
842+
{ name: 'Rick', age: offset+1, country: 'CA' },
843+
{ name: 'Alice', age: offset+2, country: 'BR' },
844+
],
845+
total: 3 // should return total amount of records in database
846+
}
847+
}
848+
849+
<Table
850+
:columns="[
851+
{ label: 'Name', fieldName: 'name' },
852+
{ label: 'Age', fieldName: 'age' },
853+
{ label: 'Country', fieldName: 'country' },
854+
]"
855+
//diff-remove
856+
:data="[...]
857+
//diff-add
858+
:data="loadPageData"
859+
860+
:pageSize="3"
861+
```
832862

833863
## ProgressBar
834864

adminforth/documentation/docs/tutorial/07-Plugins/16-email-invite.md

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -98,7 +98,7 @@ export default {
9898
//diff-add
9999
emailField: 'email',
100100
//diff-add
101-
passwordField: 'password_hash',
101+
passwordField: 'password',
102102
//diff-add
103103
sendFrom: 'noreply@yourapp.com',
104104
//diff-add
@@ -164,7 +164,7 @@ export default {
164164
new EmailInvitePlugin({
165165
emailField: 'email',
166166
sendFrom: 'noreply@yourapp.com',
167-
passwordField: 'password_hash',
167+
passwordField: 'password',
168168
adapter: new EmailAdapterAwsSes(/* ... */),
169169
//diff-add
170170
emailConfirmedField: 'email_confirmed', // Enable email confirmation
@@ -191,7 +191,7 @@ import EmailAdapterMailgun from "@adminforth/email-adapter-mailgun";
191191
plugins: [
192192
new EmailInvitePlugin({
193193
emailField: 'email',
194-
passwordField: 'password_hash',
194+
passwordField: 'password',
195195
sendFrom: 'noreply@yourapp.com',
196196
//diff-add
197197
adapter: new EmailAdapterMailgun({

adminforth/modules/configValidator.ts

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -134,11 +134,12 @@ export default class ConfigValidator implements IConfigValidator {
134134
userMenu: [],
135135
header: [],
136136
sidebar: [],
137+
sidebarTop: [],
137138
everyPageBottom: [],
138139
};
139140

140141
if (this.inputConfig.customization?.globalInjections) {
141-
const ALLOWED_GLOBAL_INJECTIONS = ['userMenu', 'header', 'sidebar', 'everyPageBottom'];
142+
const ALLOWED_GLOBAL_INJECTIONS = ['userMenu', 'header', 'sidebar', 'sidebarTop', 'everyPageBottom'];
142143
Object.keys(this.inputConfig.customization.globalInjections).forEach((injection) => {
143144
if (ALLOWED_GLOBAL_INJECTIONS.includes(injection)) {
144145
globalInjections[injection] = this.validateAndListifyInjectionNew(this.inputConfig.customization.globalInjections, injection, errors);
@@ -177,6 +178,9 @@ export default class ConfigValidator implements IConfigValidator {
177178
if (customization.showBrandNameInSidebar === undefined) {
178179
customization.showBrandNameInSidebar = true;
179180
}
181+
if (customization.showBrandLogoInSidebar === undefined) {
182+
customization.showBrandLogoInSidebar = true;
183+
}
180184
if (customization.favicon) {
181185
errors.push(...this.checkCustomFileExists(customization.favicon));
182186
}

adminforth/modules/restApi.ts

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -274,6 +274,7 @@ export default class AdminForthRestAPI implements IAdminForthRestAPI {
274274
loginPageInjections: this.adminforth.config.customization.loginPageInjections,
275275
globalInjections: {
276276
everyPageBottom: this.adminforth.config.customization.globalInjections.everyPageBottom,
277+
sidebarTop: this.adminforth.config.customization.globalInjections.sidebarTop,
277278
},
278279
rememberMeDays: this.adminforth.config.auth.rememberMeDays,
279280
singleTheme: this.adminforth.config.customization.singleTheme,
@@ -363,6 +364,7 @@ export default class AdminForthRestAPI implements IAdminForthRestAPI {
363364

364365
const loggedInPart = {
365366
showBrandNameInSidebar: this.adminforth.config.customization.showBrandNameInSidebar,
367+
showBrandLogoInSidebar: this.adminforth.config.customization.showBrandLogoInSidebar,
366368
brandLogo: this.adminforth.config.customization.brandLogo,
367369
datesFormat: this.adminforth.config.customization.datesFormat,
368370
timeFormat: this.adminforth.config.customization.timeFormat,

adminforth/modules/styles.ts

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -251,6 +251,9 @@ export const styles = () => ({
251251
lightTableOddBackground: "#FFFFFF",
252252
lightTablePaginationText: "#6B7280",
253253
lightTablePaginationNumeration: "#111827",
254+
lightTablePaginationInputBackground: "#FFFFFF",
255+
lightTablePaginationInputBorder: "#D1D5DB",
256+
lightTablePaginationInputText: "#111827",
254257
lightUnactivePaginationButtonBackground: "#FFFFFF",
255258
lightUnactivePaginationButtonText: "#6B7280",
256259
lightUnactivePaginationButtonBorder: "#D1D5DB",
@@ -580,6 +583,9 @@ export const styles = () => ({
580583
darkTableOddBackground: "#111827",
581584
darkTablePaginationText: "#9CA3AF",
582585
darkTablePaginationNumeration: "#FFFFFF",
586+
darkTablePaginationInputBackground: "#1f2937",
587+
darkTablePaginationInputBorder: "#374151",
588+
darkTablePaginationInputText: "#FFFFFF",
583589
darkUnactivePaginationButtonBackground: "#1F2937",
584590
darkUnactivePaginationButtonText: "#9CA3AF",
585591
darkUnactivePaginationButtonBorder: "#374151",

adminforth/spa/src/App.vue

Lines changed: 9 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -78,13 +78,21 @@
7878
>
7979
<div class="h-full px-3 pb-4 overflow-y-auto bg-lightSidebar dark:bg-darkSidebar border-r border-lightSidebarBorder dark:border-darkSidebarBorder">
8080
<div class="af-logo-title-wrapper flex ms-2 m-4">
81-
<img :src="loadFile(coreStore.config?.brandLogo || '@/assets/logo.svg')" :alt="`${ coreStore.config?.brandName } Logo`" class="af-logo h-8 me-3" />
81+
<img v-if="coreStore.config?.showBrandLogoInSidebar !== false" :src="loadFile(coreStore.config?.brandLogo || '@/assets/logo.svg')" :alt="`${ coreStore.config?.brandName } Logo`" class="af-logo h-8 me-3" />
8282
<span
8383
v-if="coreStore.config?.showBrandNameInSidebar"
8484
class="af-title self-center text-lightNavbarText-size font-semibold sm:text-lightNavbarText-size whitespace-nowrap dark:text-darkSidebarText text-lightSidebarText"
8585
>
8686
{{ coreStore.config?.brandName }}
8787
</span>
88+
<div class="flex items-center gap-2 w-auto" :class="{'w-full justify-end': coreStore.config?.showBrandLogoInSidebar === false}">
89+
<component
90+
v-for="c in coreStore?.config?.globalInjections?.sidebarTop || []"
91+
:is="getCustomComponent(c)"
92+
:meta="c.meta"
93+
:adminUser="coreStore.adminUser"
94+
/>
95+
</div>
8896
</div>
8997

9098
<ul class="af-sidebar-container space-y-2 font-medium">

adminforth/spa/src/adminforth.ts

Lines changed: 5 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,5 @@
1-
import type { FilterParams, FrontendAPIInterface } from "./types/FrontendAPI";
2-
import type { FrontendAPIInterface, ConfirmParams, AlertParams, } from '@/types/FrontendAPI';
3-
import type { AdminForthFilterOperators, AdminForthResourceColumn } from '@/types/Common';
1+
import type { FilterParams, FrontendAPIInterface, ConfirmParams, AlertParams, } from '@/types/FrontendAPI';
2+
import type { AdminForthFilterOperators, AdminForthResourceColumnCommon } from '@/types/Common';
43
import { useToastStore } from '@/stores/toast';
54
import { useModalStore } from '@/stores/modal';
65
import { useCoreStore } from '@/stores/core';
@@ -85,7 +84,7 @@ class FrontendAPI implements FrontendAPIInterface {
8584
}
8685
}
8786

88-
confirm(params: ConfirmParams): Promise<void> {
87+
confirm(params: ConfirmParams): Promise<boolean> {
8988
return new Promise((resolve, reject) => {
9089
this.modalStore.setModalContent({
9190
content: params.message,
@@ -112,7 +111,7 @@ class FrontendAPI implements FrontendAPIInterface {
112111
throw new Error(`Cannot use ${this.setListFilter.name} filter on a list page`)
113112
} else {
114113
console.log(this.coreStore.resourceColumnsWithFilters,'core store')
115-
const filterField = this.coreStore.resourceColumnsWithFilters.find((col: AdminForthResourceColumn) => col.name === filter.field)
114+
const filterField = this.coreStore.resourceColumnsWithFilters.find((col: AdminForthResourceColumnCommon) => col.name === filter.field)
116115
if(!filterField){
117116
throw new Error(`Field ${filter.field} is not available for filtering`)
118117
}
@@ -123,7 +122,7 @@ class FrontendAPI implements FrontendAPIInterface {
123122

124123
setListFilter(filter: FilterParams): void {
125124
if(this.listFilterValidation(filter)){
126-
if(this.filtersStore.filters.some((f) => {return f.field === filter.field && f.operator === filter.operator})){
125+
if(this.filtersStore.filters.some((f: any) => {return f.field === filter.field && f.operator === filter.operator})){
127126
throw new Error(`Filter ${filter.field} with operator ${filter.operator} already exists`)
128127
} else {
129128
this.filtersStore.setFilter(filter)

0 commit comments

Comments
 (0)