Skip to content

Commit 1db029f

Browse files
committed
Merge branch 'next' of github.com:devforth/adminforth into feature/AdminForth/893/bug-with-login-via-google
2 parents 7b6bba7 + 29fc947 commit 1db029f

File tree

16 files changed

+166
-37
lines changed

16 files changed

+166
-37
lines changed

adminforth/documentation/docs/tutorial/03-Customization/09-Actions.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -204,7 +204,7 @@ import { admin } from '../index';
204204
//diff-add
205205
await stmt.run(...selectedIds);
206206
//diff-add
207-
return { ok: true, error: false, successMessage: `Marked ${selectedIds.length} apartments as listed` };
207+
return { ok: true, successMessage: `Marked ${selectedIds.length} apartments as listed` };
208208
//diff-add
209209
},
210210
//diff-add

adminforth/documentation/docs/tutorial/03-Customization/10-menuConfiguration.md

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -23,7 +23,7 @@ E.g. create group "Blog" with Items who link to resource "posts" and "categories
2323
```ts title='./index.ts'
2424
{
2525
...
26-
menu: {
26+
menu: [
2727
{
2828
label: 'Blog',
2929
icon: 'flowbite:brain-solid',
@@ -46,7 +46,7 @@ E.g. create group "Blog" with Items who link to resource "posts" and "categories
4646
icon: 'flowbite:folder-duplicate-outline',
4747
resourceId: 'adminuser',
4848
},
49-
},
49+
],
5050
...
5151
}
5252
```

adminforth/documentation/docs/tutorial/03-Customization/13-standardPagesTuning.md

Lines changed: 1 addition & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -63,7 +63,7 @@ export default {
6363
}
6464
```
6565

66-
You can also specify on which page you want to create or delete groups. If you assign null, the groups will disappear from this page.
66+
You can also specify on which page you want to create groups.
6767

6868
```typescript title="./resources/apartments.ts"
6969
export default {
@@ -89,10 +89,6 @@ export default {
8989
}
9090
//diff-add
9191
],
92-
//diff-add
93-
editFieldGroups: null,
94-
//diff-add
95-
showFieldGroups: null,
9692
}
9793
}
9894
```

adminforth/modules/configValidator.ts

Lines changed: 10 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -807,17 +807,23 @@ export default class ConfigValidator implements IConfigValidator {
807807
if (typeof condition !== 'object') {
808808
return;
809809
}
810+
const relatedColumn = res.columns.find((c) => c.name === field);
811+
if (!relatedColumn) {
812+
const similar = suggestIfTypo(res.columns.map((c) => c.name), field);
813+
errors.push(`Resource "${res.resourceId}" column "${column.name}" has showIf on unknown column "${field}". ${similar ? `Did you mean "${similar}"?` : ''}`);
814+
return;
815+
}
810816
if ("$in" in condition && !Array.isArray(condition.$in)) {
811817
errors.push(`Resource "${res.resourceId}" column "${column.name}" has showIf with $in that is not an array`);
812818
}
813819
if ("$nin" in condition && !Array.isArray(condition.$nin)) {
814820
errors.push(`Resource "${res.resourceId}" column "${column.name}" has showIf with $nin that is not an array`);
815821
}
816-
if ("$includes" in condition && !column.isArray) {
817-
errors.push(`Resource "${res.resourceId}" has showIf with $includes on non-array column "${column.name}"`);
822+
if ("$includes" in condition && !relatedColumn.isArray?.enabled) {
823+
errors.push(`Resource "${res.resourceId}" has showIf with $includes on non-array column "${relatedColumn.name}"`);
818824
}
819-
if ("$nincludes" in condition && !column.isArray) {
820-
errors.push(`Resource "${res.resourceId}" has showIf with $nincludes on non-array column "${column.name}"`);
825+
if ("$nincludes" in condition && !relatedColumn.isArray?.enabled) {
826+
errors.push(`Resource "${res.resourceId}" has showIf with $nincludes on non-array column "${relatedColumn.name}"`);
821827
}
822828
});
823829
}

adminforth/modules/restApi.ts

Lines changed: 25 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@ import {
1212
HttpExtra,
1313
IAdminForthAndOrFilter,
1414
BackendOnlyInput,
15+
Filters,
1516
} from "../types/Back.js";
1617

1718
import { ADMINFORTH_VERSION, listify, md5hash, getLoginPromptHTML } from './utils.js';
@@ -363,7 +364,13 @@ export default class AdminForthRestAPI implements IAdminForthRestAPI {
363364

364365
const announcementBadge: AnnouncementBadgeResponse = this.adminforth.config.customization.announcementBadge?.(adminUser);
365366

366-
367+
const settingPages = []
368+
for ( const settingPage of this.adminforth.config.auth.userMenuSettingsPages || [] ) {
369+
if ( settingPage.isVisible ) {
370+
const isVisible = await settingPage.isVisible( adminUser );
371+
settingPages.push( { ...settingPage, isVisible } );
372+
}
373+
}
367374

368375
const publicPart = {
369376
brandName: this.adminforth.config.customization.brandName,
@@ -378,7 +385,6 @@ export default class AdminForthRestAPI implements IAdminForthRestAPI {
378385
singleTheme: this.adminforth.config.customization.singleTheme,
379386
customHeadItems: this.adminforth.config.customization.customHeadItems,
380387
}
381-
382388
const loggedInPart = {
383389
showBrandNameInSidebar: this.adminforth.config.customization.showBrandNameInSidebar,
384390
showBrandLogoInSidebar: this.adminforth.config.customization.showBrandLogoInSidebar,
@@ -393,7 +399,7 @@ export default class AdminForthRestAPI implements IAdminForthRestAPI {
393399
announcementBadge,
394400
globalInjections: this.adminforth.config.customization.globalInjections,
395401
userFullnameField: this.adminforth.config.auth.userFullNameField,
396-
settingPages: this.adminforth.config.auth.userMenuSettingsPages,
402+
settingPages: settingPages,
397403
}
398404

399405
// translate menu labels
@@ -1175,6 +1181,14 @@ export default class AdminForthRestAPI implements IAdminForthRestAPI {
11751181
}
11761182
}
11771183

1184+
const primaryKeyColumn = resource.columns.find((col) => col.primaryKey);
1185+
if (record[primaryKeyColumn.name] !== undefined) {
1186+
const existingRecord = await this.adminforth.resource(resource.resourceId).get([Filters.EQ(primaryKeyColumn.name, record[primaryKeyColumn.name])]);
1187+
if (existingRecord) {
1188+
return { error: `Record with ${primaryKeyColumn.name} '${record[primaryKeyColumn.name]}' already exists`, ok: false };
1189+
}
1190+
}
1191+
11781192
const ctxCreate = {
11791193
adminUser,
11801194
resource,
@@ -1291,6 +1305,14 @@ export default class AdminForthRestAPI implements IAdminForthRestAPI {
12911305
return { error: allowedError };
12921306
}
12931307

1308+
const primaryKeyColumn = resource.columns.find((col) => col.primaryKey);
1309+
if (record[primaryKeyColumn.name] !== undefined) {
1310+
const existingRecord = await this.adminforth.resource(resource.resourceId).get([Filters.EQ(primaryKeyColumn.name, record[primaryKeyColumn.name])]);
1311+
if (existingRecord) {
1312+
return { error: `Record with ${primaryKeyColumn.name} '${record[primaryKeyColumn.name]}' already exists`, ok: false };
1313+
}
1314+
}
1315+
12941316
const ctxEdit = {
12951317
adminUser,
12961318
resource,

adminforth/spa/src/App.vue

Lines changed: 73 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
<template>
22
<div>
33
<nav
4-
v-if="loggedIn && routerIsReady && loginRedirectCheckIsReady && defaultLayout"
4+
v-if="loggedIn && routerIsReady && loginRedirectCheckIsReady && defaultLayout && headerOnlyLayout"
55
class="fixed h-14 top-0 z-30 w-full border-b shadow-sm bg-lightNavbar shadow-headerShadow dark:bg-darkNavbar dark:border-darkSidebarDevider"
66
>
77
<div class="af-header px-3 lg:px-5 lg:pl-3 flex items-center justify-between h-full w-full" >
@@ -73,7 +73,7 @@
7373
</nav>
7474

7575
<Sidebar
76-
v-if="loggedIn && routerIsReady && loginRedirectCheckIsReady && defaultLayout"
76+
v-if="loggedIn && routerIsReady && loginRedirectCheckIsReady && defaultLayout && !headerOnlyLayout"
7777
:sideBarOpen="sideBarOpen"
7878
:forceIconOnly="route.meta?.sidebarAndHeader === 'preferIconOnly'"
7979
@hideSidebar="hideSidebar"
@@ -183,6 +183,7 @@ initFrontedAPI()
183183
createHead()
184184
const sideBarOpen = ref(false);
185185
const defaultLayout = ref(true);
186+
const headerOnlyLayout = ref(false);
186187
const route = useRoute();
187188
const router = useRouter();
188189
const publicConfigLoaded = ref(false);
@@ -201,10 +202,77 @@ const expandedWidth = computed(() => coreStore.config?.iconOnlySidebar?.expanded
201202
const theme = ref('light');
202203
203204
const userMenuComponents = computed(() => {
204-
console.log('🪲🆕 userMenuComponents recomputed', coreStore?.config?.globalInjections?.userMenu);
205+
console.log('🪲🆕 userMenuComponents recomputed', JSON.parse(JSON.stringify(coreStore?.config?.globalInjections?.userMenu)));
205206
return coreStore?.config?.globalInjections?.userMenu || [];
206207
})
207208
209+
watch(
210+
() => coreStore.config?.globalInjections?.userMenu,
211+
(newVal, oldVal) => {
212+
// Only log when it becomes undefined (you can relax this if needed)
213+
if (newVal === undefined) {
214+
const err = new Error('🔍 userMenu changed to undefined');
215+
console.groupCollapsed(
216+
'%c[TRACE] userMenu changed to undefined',
217+
'color: red; font-weight: bold;'
218+
);
219+
console.log('old value:', oldVal);
220+
console.log('new value:', newVal);
221+
console.log('coreStore.config.globalInjections:', coreStore.config?.globalInjections);
222+
console.log('Stack trace:');
223+
console.log(err.stack);
224+
console.groupEnd();
225+
} else {
226+
// Optional: log ALL changes for debugging
227+
console.groupCollapsed(
228+
'%c[DEBUG] userMenu changed',
229+
'color: orange; font-weight: bold;'
230+
);
231+
console.log('old value:', oldVal);
232+
console.log('new value:', newVal);
233+
console.log('coreStore.config.globalInjections:', coreStore.config?.globalInjections);
234+
console.groupEnd();
235+
}
236+
},
237+
{
238+
deep: false,
239+
immediate: false,
240+
}
241+
);
242+
243+
watch(() => coreStore.config?.globalInjections, (v) => {
244+
console.log("🔧 globalInjections replaced:", v);
245+
}, { deep: false });
246+
247+
watch(
248+
() => coreStore.config?.globalInjections?.userMenu,
249+
(newVal, oldVal) => {
250+
if (newVal === undefined) {
251+
const err = new Error('🔍 userMenu changed to undefined');
252+
console.groupCollapsed(
253+
'%c[TRACE] userMenu changed to undefined',
254+
'color: red; font-weight: bold;'
255+
);
256+
console.log('old value:', oldVal);
257+
console.log('new value:', newVal);
258+
console.log('coreStore.config.globalInjections:', coreStore.config?.globalInjections);
259+
console.log('Stack trace:');
260+
console.log(err.stack);
261+
console.groupEnd();
262+
} else {
263+
console.groupCollapsed(
264+
'%c[DEBUG] userMenu changed',
265+
'color: orange; font-weight: bold;'
266+
);
267+
console.log('old value:', oldVal);
268+
console.log('new value:', newVal);
269+
console.log('coreStore.config.globalInjections:', coreStore.config?.globalInjections);
270+
console.groupEnd();
271+
}
272+
},
273+
{ deep: false, immediate: false }
274+
);
275+
208276
function hideSidebar(): void {
209277
sideBarOpen.value = false;
210278
}
@@ -246,6 +314,8 @@ function handleCustomLayout() {
246314
} else if (route.meta?.sidebarAndHeader === 'preferIconOnly') {
247315
defaultLayout.value = true;
248316
isSidebarIconOnly.value = true;
317+
} else if (route.meta?.sidebarAndHeader === 'headerOnly') {
318+
headerOnlyLayout.value = true;
249319
} else {
250320
defaultLayout.value = true;
251321
}

adminforth/spa/src/components/GroupsTable.vue

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -55,7 +55,7 @@
5555
@update:emptiness="customComponentsEmptiness[$event.name] = $event.value"
5656
:readonly="readonlyColumns?.includes(column.name)"
5757
/>
58-
<div v-if="columnError(column) && validating" class="mt-1 text-xs text-lightInputErrorColor dark:text-darkInputErrorColor">{{ columnError(column) }}</div>
58+
<div v-if="columnError(column) && validating" class="af-invalid-field-message mt-1 text-xs text-lightInputErrorColor dark:text-darkInputErrorColor">{{ columnError(column) }}</div>
5959
<div v-if="column.editingNote && column.editingNote[mode]" class="mt-1 text-xs text-lightFormFieldTextColor dark:text-darkFormFieldTextColor">{{ column.editingNote[mode] }}</div>
6060
</td>
6161
</tr>

adminforth/spa/src/components/ResourceForm.vue

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -377,4 +377,9 @@ watch(() => isValid.value, (value) => {
377377
emit('update:isValid', value);
378378
});
379379
380+
defineExpose({
381+
columnError,
382+
editableColumns,
383+
})
384+
380385
</script>

adminforth/spa/src/components/ResourceListTable.vue

Lines changed: 6 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,7 @@
1515
<tbody>
1616
<!-- table header -->
1717
<tr class="t-header sticky z-20 top-0 text-xs text-lightListTableHeadingText bg-lightListTableHeading dark:bg-darkListTableHeading dark:text-darkListTableHeadingText">
18-
<td scope="col" class="p-4 sticky-column bg-lightListTableHeading dark:bg-darkListTableHeading">
18+
<td scope="col" class="list-table-header-cell p-4 sticky-column bg-lightListTableHeading dark:bg-darkListTableHeading">
1919
<Checkbox
2020
:modelValue="allFromThisPageChecked"
2121
:disabled="!rows || !rows.length"
@@ -25,7 +25,7 @@
2525
</Checkbox>
2626
</td>
2727

28-
<td v-for="c in columnsListed" ref="headerRefs" scope="col" class="px-2 md:px-3 lg:px-6 py-3" :class="{'sticky-column bg-lightListTableHeading dark:bg-darkListTableHeading': c.listSticky}">
28+
<td v-for="c in columnsListed" ref="headerRefs" scope="col" class="list-table-header-cell px-2 md:px-3 lg:px-6 py-3" :class="{'sticky-column bg-lightListTableHeading dark:bg-darkListTableHeading': c.listSticky}">
2929

3030
<div @click="(evt) => c.sortable && onSortButtonClick(evt, c.name)"
3131
class="flex items-center " :class="{'cursor-pointer':c.sortable}">
@@ -86,7 +86,7 @@
8686
<tr @click="onClick($event,row)"
8787
v-else v-for="(row, rowI) in rows" :key="`row_${row._primaryKeyValue}`"
8888
ref="rowRefs"
89-
class="bg-lightListTable dark:bg-darkListTable border-lightListBorder dark:border-gray-700 hover:bg-lightListTableRowHover dark:hover:bg-darkListTableRowHover"
89+
class="list-table-body-row bg-lightListTable dark:bg-darkListTable border-lightListBorder dark:border-gray-700 hover:bg-lightListTableRowHover dark:hover:bg-darkListTableRowHover"
9090

9191
:class="{'border-b': rowI !== rows.length - 1, 'cursor-pointer': row._clickUrl !== null}"
9292
>
@@ -620,9 +620,10 @@ td.sticky-column {
620620
@apply left-[56px];
621621
}
622622
}
623-
tr:not(:first-child):hover {
624-
td.sticky-column {
623+
tr.list-table-body-row:not(:first-child):hover {
624+
td.sticky-column:not(.list-table-header-cell) {
625625
@apply bg-lightListTableRowHover dark:bg-darkListTableRowHover;
626626
}
627627
}
628+
628629
</style>

adminforth/spa/src/components/ResourceListTableVirtual.vue

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -19,7 +19,7 @@
1919
<tbody>
2020
<!-- table header -->
2121
<tr class="t-header sticky z-20 top-0 text-xs bg-lightListTableHeading dark:bg-darkListTableHeading dark:text-gray-400">
22-
<td scope="col" class="p-4 sticky-column bg-lightListTableHeading dark:bg-darkListTableHeading">
22+
<td scope="col" class="list-table-header-cell p-4 sticky-column bg-lightListTableHeading dark:bg-darkListTableHeading">
2323
<Checkbox
2424
:modelValue="allFromThisPageChecked"
2525
:disabled="!rows || !rows.length"
@@ -29,7 +29,7 @@
2929
</Checkbox>
3030
</td>
3131

32-
<td v-for="c in columnsListed" ref="headerRefs" scope="col" class="px-2 md:px-3 lg:px-6 py-3" :class="{'sticky-column bg-lightListTableHeading dark:bg-darkListTableHeading': c.listSticky}">
32+
<td v-for="c in columnsListed" ref="headerRefs" scope="col" class="list-table-header-cell px-2 md:px-3 lg:px-6 py-3" :class="{'sticky-column bg-lightListTableHeading dark:bg-darkListTableHeading': c.listSticky}">
3333

3434
<div @click="(evt) => c.sortable && onSortButtonClick(evt, c.name)"
3535
class="flex items-center " :class="{'cursor-pointer':c.sortable}">
@@ -97,7 +97,7 @@
9797
v-for="(row, rowI) in visibleRows"
9898
:key="`row_${row._primaryKeyValue}`"
9999
ref="rowRefs"
100-
class="bg-lightListTable dark:bg-darkListTable border-lightListBorder dark:border-gray-700 hover:bg-lightListTableRowHover dark:hover:bg-darkListTableRowHover"
100+
class="list-table-body-row bg-lightListTable dark:bg-darkListTable border-lightListBorder dark:border-gray-700 hover:bg-lightListTableRowHover dark:hover:bg-darkListTableRowHover"
101101
:class="{'border-b': rowI !== visibleRows.length - 1, 'cursor-pointer': row._clickUrl !== null}"
102102
@mounted="(el: any) => updateRowHeight(`row_${row._primaryKeyValue}`, el.offsetHeight)"
103103
>
@@ -761,8 +761,8 @@ td.sticky-column {
761761
@apply left-[56px];
762762
}
763763
}
764-
tr:not(:first-child):hover {
765-
td.sticky-column {
764+
tr.list-table-body-row:not(:first-child):hover {
765+
td.sticky-column:not(.list-table-header-cell) {
766766
@apply bg-lightListTableRowHover dark:bg-darkListTableRowHover;
767767
}
768768
}

0 commit comments

Comments
 (0)