adminforth 2.5.2 → 2.6.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/commands/createApp/templates/index.ts.hbs +7 -0
- package/commands/createCustomComponent/configLoader.js +3 -0
- package/dist/dataConnectors/baseConnector.d.ts.map +1 -1
- package/dist/dataConnectors/baseConnector.js +16 -3
- package/dist/dataConnectors/baseConnector.js.map +1 -1
- package/dist/dataConnectors/mongo.d.ts.map +1 -1
- package/dist/dataConnectors/mongo.js +13 -6
- package/dist/dataConnectors/mongo.js.map +1 -1
- package/dist/index.d.ts +1 -0
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +19 -8
- package/dist/index.js.map +1 -1
- package/dist/modules/codeInjector.d.ts.map +1 -1
- package/dist/modules/codeInjector.js +20 -3
- package/dist/modules/codeInjector.js.map +1 -1
- package/dist/modules/configValidator.d.ts.map +1 -1
- package/dist/modules/configValidator.js +48 -1
- package/dist/modules/configValidator.js.map +1 -1
- package/dist/modules/restApi.d.ts.map +1 -1
- package/dist/modules/restApi.js +145 -25
- package/dist/modules/restApi.js.map +1 -1
- package/dist/modules/styles.d.ts +450 -13
- package/dist/modules/styles.d.ts.map +1 -1
- package/dist/modules/styles.js +506 -31
- package/dist/modules/styles.js.map +1 -1
- package/dist/modules/utils.d.ts +1 -0
- package/dist/modules/utils.d.ts.map +1 -1
- package/dist/modules/utils.js +9 -0
- package/dist/modules/utils.js.map +1 -1
- package/dist/spa/index.html +1 -1
- package/dist/spa/src/App.vue +21 -11
- package/dist/spa/src/afcl/Button.vue +3 -3
- package/dist/spa/src/afcl/Checkbox.vue +21 -13
- package/dist/spa/src/afcl/CountryFlag.vue +4 -1
- package/dist/spa/src/{components/CustomDatePicker.vue → afcl/DatePicker.vue} +95 -9
- package/dist/spa/src/afcl/Dialog.vue +6 -6
- package/dist/spa/src/afcl/Dropzone.vue +10 -10
- package/dist/spa/src/afcl/Input.vue +4 -4
- package/dist/spa/src/afcl/ProgressBar.vue +7 -7
- package/dist/spa/src/afcl/Select.vue +51 -21
- package/dist/spa/src/afcl/Skeleton.vue +6 -6
- package/dist/spa/src/afcl/Table.vue +11 -11
- package/dist/spa/src/afcl/Toggle.vue +32 -0
- package/dist/spa/src/afcl/Tooltip.vue +1 -1
- package/dist/spa/src/afcl/VerticalTabs.vue +3 -3
- package/dist/spa/src/afcl/index.ts +2 -1
- package/dist/spa/src/components/AcceptModal.vue +6 -6
- package/dist/spa/src/components/Breadcrumbs.vue +5 -5
- package/dist/spa/src/components/ColumnValueInput.vue +28 -9
- package/dist/spa/src/components/ColumnValueInputWrapper.vue +2 -1
- package/dist/spa/src/components/CustomDateRangePicker.vue +9 -8
- package/dist/spa/src/components/CustomRangePicker.vue +32 -3
- package/dist/spa/src/components/Filters.vue +76 -31
- package/dist/spa/src/components/GroupsTable.vue +7 -7
- package/dist/spa/src/components/ResourceForm.vue +61 -26
- package/dist/spa/src/components/ResourceListTable.vue +28 -29
- package/dist/spa/src/components/ResourceListTableVirtual.vue +25 -27
- package/dist/spa/src/components/ShowTable.vue +8 -6
- package/dist/spa/src/components/SingleSkeletLoader.vue +6 -6
- package/dist/spa/src/components/SkeleteLoader.vue +1 -1
- package/dist/spa/src/components/ThreeDotsMenu.vue +5 -5
- package/dist/spa/src/components/Toast.vue +2 -7
- package/dist/spa/src/components/ValueRenderer.vue +4 -4
- package/dist/spa/src/controls/BoolToggle.vue +34 -0
- package/dist/spa/src/spa_types/core.ts +7 -0
- package/dist/spa/src/stores/core.ts +1 -1
- package/dist/spa/src/types/Back.ts +71 -10
- package/dist/spa/src/types/Common.ts +12 -7
- package/dist/spa/src/utils.ts +209 -0
- package/dist/spa/src/views/CreateView.vue +4 -4
- package/dist/spa/src/views/EditView.vue +3 -3
- package/dist/spa/src/views/ListView.vue +13 -18
- package/dist/spa/src/views/LoginView.vue +22 -24
- package/dist/spa/src/views/ResourceParent.vue +1 -1
- package/dist/spa/src/views/ShowView.vue +3 -3
- package/dist/types/Back.d.ts +55 -8
- package/dist/types/Back.d.ts.map +1 -1
- package/dist/types/Back.js.map +1 -1
- package/dist/types/Common.d.ts +11 -6
- package/dist/types/Common.d.ts.map +1 -1
- package/dist/types/Common.js.map +1 -1
- package/package.json +1 -1
|
@@ -5,28 +5,28 @@
|
|
|
5
5
|
:style="`height: ${containerHeight}px; will-change: transform;`"
|
|
6
6
|
@scroll="handleScroll"
|
|
7
7
|
ref="containerRef"
|
|
8
|
-
>
|
|
8
|
+
>
|
|
9
9
|
<!-- skelet loader -->
|
|
10
10
|
<div role="status" v-if="!resource || !resource.columns"
|
|
11
11
|
class="max-w p-4 space-y-4 divide-y divide-gray-200 rounded shadow animate-pulse dark:divide-gray-700 md:p-6 dark:border-gray-700">
|
|
12
12
|
|
|
13
13
|
<div role="status" class="max-w-sm animate-pulse">
|
|
14
|
-
<div class="h-2 bg-
|
|
14
|
+
<div class="h-2 bg-lightListSkeletLoader rounded-full dark:bg-darkListSkeletLoader max-w-[360px]"></div>
|
|
15
15
|
</div>
|
|
16
16
|
</div>
|
|
17
|
-
<table v-else class=" w-full text-sm text-left rtl:text-right text-
|
|
17
|
+
<table v-else class=" w-full text-sm text-left rtl:text-right text-lightListTableText dark:text-darkListTableText rounded-default">
|
|
18
18
|
|
|
19
19
|
<tbody>
|
|
20
20
|
<!-- table header -->
|
|
21
21
|
<tr class="t-header sticky z-10 top-0 text-xs bg-lightListTableHeading dark:bg-darkListTableHeading dark:text-gray-400">
|
|
22
22
|
<td scope="col" class="p-4">
|
|
23
|
-
<
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
<
|
|
29
|
-
</
|
|
23
|
+
<Checkbox
|
|
24
|
+
:modelValue="allFromThisPageChecked"
|
|
25
|
+
:disabled="!rows || !rows.length"
|
|
26
|
+
@update:modelValue="selectAll"
|
|
27
|
+
>
|
|
28
|
+
<span class="sr-only">{{ $t('checkbox') }}</span>
|
|
29
|
+
</Checkbox>
|
|
30
30
|
</td>
|
|
31
31
|
|
|
32
32
|
<td v-for="c in columnsListed" ref="headerRefs" scope="col" class="px-2 md:px-3 lg:px-6 py-3">
|
|
@@ -101,17 +101,14 @@
|
|
|
101
101
|
:class="{'border-b': rowI !== visibleRows.length - 1, 'cursor-pointer': row._clickUrl !== null}"
|
|
102
102
|
@mounted="(el) => updateRowHeight(`row_${row._primaryKeyValue}`, el.offsetHeight)"
|
|
103
103
|
>
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
class="w-4 h-4 text-blue-600 bg-gray-100 border-gray-300 rounded focus:ring-blue-500 dark:focus:ring-blue-600 dark:ring-offset-gray-800 dark:focus:ring-offset-gray-800 focus:ring-2 dark:bg-gray-700 dark:border-gray-600 cursor-pointer">
|
|
113
|
-
<label for="checkbox-table-search-1" class="sr-only">{{ $t('checkbox') }}</label>
|
|
114
|
-
</div>
|
|
104
|
+
<td class="w-4 p-4 cursor-default" @click="(e)=>e.stopPropagation()">
|
|
105
|
+
<Checkbox
|
|
106
|
+
:model-value="checkboxesInternal.includes(row._primaryKeyValue)"
|
|
107
|
+
@change="(e)=>{addToCheckedValues(row._primaryKeyValue)}"
|
|
108
|
+
@click="(e)=>e.stopPropagation()"
|
|
109
|
+
>
|
|
110
|
+
<span class="sr-only">{{ $t('checkbox') }}</span>
|
|
111
|
+
</Checkbox>
|
|
115
112
|
</td>
|
|
116
113
|
<td v-for="c in columnsListed" class="px-2 md:px-3 lg:px-6 py-4">
|
|
117
114
|
<!-- if c.name in listComponentsPerColumn, render it. If not, render ValueRenderer -->
|
|
@@ -223,7 +220,7 @@
|
|
|
223
220
|
>
|
|
224
221
|
<!-- Buttons -->
|
|
225
222
|
<button
|
|
226
|
-
class="flex items-center py-1 px-3 gap-1 text-sm font-medium text-
|
|
223
|
+
class="af-pagination-prev-button flex items-center py-1 px-3 gap-1 text-sm font-medium text-lightListTablePaginationText focus:outline-none bg-lightListTablePaginationBackgoround border-r-0 rounded-s border border-lightListTablePaginationBorder hover:bg-lightListTablePaginationBackgoroundHover hover:text-lightListTablePaginationTextHover focus:z-10 focus:ring-4 focus:ring-lightListTablePaginationFocusRing dark:focus:ring-darkListTablePaginationFocusRing dark:bg-darkListTablePaginationBackgoround dark:text-darkListTablePaginationText dark:border-darkListTablePaginationBorder dark:hover:text-darkListTablePaginationTextHover dark:hover:bg-darkListTablePaginationBackgoroundHover disabled:opacity-50"
|
|
227
224
|
@click="page--; pageInput = page.toString();" :disabled="page <= 1">
|
|
228
225
|
<svg class="w-3.5 h-3.5 rtl:rotate-180" aria-hidden="true" xmlns="http://www.w3.org/2000/svg" fill="none"
|
|
229
226
|
viewBox="0 0 14 10">
|
|
@@ -235,14 +232,14 @@
|
|
|
235
232
|
</span>
|
|
236
233
|
</button>
|
|
237
234
|
<button
|
|
238
|
-
class="flex items-center py-1 px-3 text-sm font-medium text-
|
|
235
|
+
class="af-pagination-first-page-button flex items-center py-1 px-3 text-sm font-medium text-lightListTablePaginationText focus:outline-none bg-lightListTablePaginationBackgoround border-r-0 border border-lightListTablePaginationBorder hover:bg-lightListTablePaginationBackgoroundHover hover:text-lightListTablePaginationTextHover focus:z-10 focus:ring-4 focus:ring-lightListTablePaginationFocusRing dark:focus:ring-darkListTablePaginationFocusRing dark:bg-darkListTablePaginationBackgoround dark:text-darkListTablePaginationText dark:border-darkListTablePaginationBorder dark:hover:text-darkListTablePaginationTextHover dark:hover:bg-darkListTablePaginationBackgoroundHover disabled:opacity-50"
|
|
239
236
|
@click="page = 1; pageInput = page.toString();" :disabled="page <= 1">
|
|
240
237
|
<!-- <IconChevronDoubleLeftOutline class="w-4 h-4" /> -->
|
|
241
238
|
1
|
|
242
239
|
</button>
|
|
243
240
|
<div
|
|
244
241
|
contenteditable="true"
|
|
245
|
-
class="min-w-10 outline-none inline-block w-auto
|
|
242
|
+
class="af-pagination-input min-w-10 outline-none inline-block w-auto py-1.5 px-3 text-sm text-center text-lightListTablePaginationCurrentPageText border border-lightListTablePaginationBorder dark:border-darkListTablePaginationBorder dark:text-darkListTablePaginationCurrentPageText dark:bg-darkListTablePaginationBackgoround z-10"
|
|
246
243
|
@keydown="onPageKeydown($event)"
|
|
247
244
|
@input="onPageInput($event)"
|
|
248
245
|
@blur="validatePageInput()"
|
|
@@ -251,14 +248,14 @@
|
|
|
251
248
|
</div>
|
|
252
249
|
|
|
253
250
|
<button
|
|
254
|
-
class="flex items-center py-1 px-3 text-sm font-medium text-
|
|
251
|
+
class="af-pagination-last-page-button flex items-center py-1 px-3 text-sm font-medium text-lightListTablePaginationText focus:outline-none bg-lightListTablePaginationBackgoround border-l-0 border border-lightListTablePaginationBorder hover:bg-lightListTablePaginationBackgoroundHover hover:text-lightListTablePaginationTextHover focus:z-10 focus:ring-4 focus:ring-lightListTablePaginationFocusRing dark:focus:ring-darkListTablePaginationFocusRing dark:bg-darkListTablePaginationBackgoround dark:text-darkListTablePaginationText dark:border-darkListTablePaginationBorder dark:hover:text-white dark:hover:bg-darkListTablePaginationBackgoroundHover disabled:opacity-50"
|
|
255
252
|
@click="page = totalPages; pageInput = page.toString();" :disabled="page >= totalPages">
|
|
256
253
|
{{ totalPages }}
|
|
257
254
|
|
|
258
255
|
<!-- <IconChevronDoubleRightOutline class="w-4 h-4" /> -->
|
|
259
256
|
</button>
|
|
260
257
|
<button
|
|
261
|
-
class="flex items-center py-1 px-3 gap-1 text-sm font-medium text-
|
|
258
|
+
class="af-pagination-next-button flex items-center py-1 px-3 gap-1 text-sm font-medium text-lightListTablePaginationText focus:outline-none bg-lightListTablePaginationBackgoround border-l-0 rounded-e border border-lightListTablePaginationBorder hover:bg-lightListTablePaginationBackgoroundHover hover:text-lightListTablePaginationTextHover focus:z-10 focus:ring-4 focus:ring-lightListTablePaginationFocusRing dark:focus:ring-darkListTablePaginationFocusRing dark:bg-darkListTablePaginationBackgoround dark:text-darkListTablePaginationText dark:border-darkListTablePaginationBorder dark:hover:text-white dark:hover:bg-darkListTablePaginationBackgoroundHover disabled:opacity-50"
|
|
262
259
|
@click="page++; pageInput = page.toString();" :disabled="page >= totalPages">
|
|
263
260
|
<span class="hidden sm:inline">{{ $t('Next') }}</span>
|
|
264
261
|
<svg class="w-3.5 h-3.5 rtl:rotate-180" aria-hidden="true" xmlns="http://www.w3.org/2000/svg" fill="none"
|
|
@@ -270,7 +267,7 @@
|
|
|
270
267
|
</div>
|
|
271
268
|
|
|
272
269
|
<!-- Help text -->
|
|
273
|
-
<span class="text-sm text-
|
|
270
|
+
<span class="text-sm text-lightListTablePaginationHelpText dark:text-darkListTablePaginationHelpText">
|
|
274
271
|
<span v-if="((((page || 1) - 1) * pageSize + 1 > totalRows) && totalRows > 0)">{{ $t('Wrong Page') }} </span>
|
|
275
272
|
<template v-else-if="resource && totalRows > 0">
|
|
276
273
|
|
|
@@ -330,6 +327,7 @@ import router from '@/router';
|
|
|
330
327
|
import { Tooltip } from '@/afcl';
|
|
331
328
|
import type { AdminForthResourceCommon } from '@/types/Common';
|
|
332
329
|
import adminforth from '@/adminforth';
|
|
330
|
+
import Checkbox from '@/afcl/Checkbox.vue';
|
|
333
331
|
|
|
334
332
|
const coreStore = useCoreStore();
|
|
335
333
|
const { t } = useI18n();
|
|
@@ -1,10 +1,12 @@
|
|
|
1
1
|
<template>
|
|
2
|
-
<div
|
|
3
|
-
|
|
2
|
+
<div class="overflow-x-auto shadow-resourseFormShadow dark:shadow-darkResourseFormShadow"
|
|
3
|
+
:class="{'rounded-default' : isRounded}"
|
|
4
|
+
>
|
|
5
|
+
<div v-if="groupName && !noTitle" class="text-md font-semibold px-6 py-3 flex flex-1 items-center text-lightShowTableHeadingText bg-lightShowTableHeadingBackground dark:bg-darkShowTableHeadingBackground dark:text-darkShowTableHeadingText rounded-t-lg">
|
|
4
6
|
{{ groupName }}
|
|
5
7
|
</div>
|
|
6
|
-
<table class="w-full text-sm text-left rtl:text-right text-
|
|
7
|
-
<thead v-if="!allColumnsHaveCustomComponent" class="text-
|
|
8
|
+
<table class="w-full text-sm text-left rtl:text-right text-lightShowTableBodyText dark:text-darkShowTableBodyText table-fixed">
|
|
9
|
+
<thead v-if="!allColumnsHaveCustomComponent" class="text-lightShowTableUnderHeadingText dark:text-darkShowTableUnderHeadingText bg-lightShowTableUnderHeadingBackground dark:bg-darkShowTableUnderHeadingBackground dark:border-darkFormBorder block md:table-row-group">
|
|
8
10
|
<tr>
|
|
9
11
|
<th scope="col" class="px-6 py-3 text-xs uppercase hidden md:w-52 md:table-cell">
|
|
10
12
|
{{ $t('Field') }}
|
|
@@ -18,8 +20,8 @@
|
|
|
18
20
|
<tr
|
|
19
21
|
v-for="column in columns"
|
|
20
22
|
:key="column.name"
|
|
21
|
-
class="bg-
|
|
22
|
-
dark:bg-
|
|
23
|
+
class="bg-lightShowTablesBodyBackground border-t border-lightShowTableBodyBorder
|
|
24
|
+
dark:bg-darkShowTablesBodyBackground dark:border-darkShowTableBodyBorder block md:table-row"
|
|
23
25
|
>
|
|
24
26
|
<component
|
|
25
27
|
v-if="column.components?.showRow"
|
|
@@ -2,12 +2,12 @@
|
|
|
2
2
|
<div
|
|
3
3
|
role="status" class="max-w-sm animate-pulse"
|
|
4
4
|
>
|
|
5
|
-
<div class="h-2.5 bg-
|
|
6
|
-
<div class="h-2 bg-
|
|
7
|
-
<div class="h-2 bg-
|
|
8
|
-
<div class="h-2 bg-
|
|
9
|
-
<div class="h-2 bg-
|
|
10
|
-
<div class="h-2 bg-
|
|
5
|
+
<div class="h-2.5 bg-lightSkeletonBackgroundColor rounded-full dark:bg-darkSkeletonBackgroundColor w-48 mb-4"></div>
|
|
6
|
+
<div class="h-2 bg-lightSkeletonBackgroundColor rounded-full dark:bg-darkSkeletonBackgroundColor max-w-[360px] mb-2.5"></div>
|
|
7
|
+
<div class="h-2 bg-lightSkeletonBackgroundColor rounded-full dark:bg-darkSkeletonBackgroundColor mb-2.5"></div>
|
|
8
|
+
<div class="h-2 bg-lightSkeletonBackgroundColor rounded-full dark:bg-darkSkeletonBackgroundColor max-w-[330px] mb-2.5"></div>
|
|
9
|
+
<div class="h-2 bg-lightSkeletonBackgroundColor rounded-full dark:bg-darkSkeletonBackgroundColor max-w-[300px] mb-2.5"></div>
|
|
10
|
+
<div class="h-2 bg-lightSkeletonBackgroundColor rounded-full dark:bg-darkSkeletonBackgroundColor max-w-[360px]"></div>
|
|
11
11
|
<span class="sr-only">{{ $t('Loading...') }}</span>
|
|
12
12
|
</div>
|
|
13
13
|
</template>
|
|
@@ -12,7 +12,7 @@
|
|
|
12
12
|
: '']"
|
|
13
13
|
>
|
|
14
14
|
<div role="status" class="max-w-sm animate-pulse">
|
|
15
|
-
<div class="h-2 bg-
|
|
15
|
+
<div class="h-2 bg-lightSkeletonBackgroundColor rounded-full dark:bg-darkSkeletonBackgroundColor max-w-[360px]"></div>
|
|
16
16
|
</div>
|
|
17
17
|
</td>
|
|
18
18
|
</tr>
|
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
<template v-if="threeDotsDropdownItems?.length || customActions?.length">
|
|
3
3
|
<button
|
|
4
4
|
data-dropdown-toggle="listThreeDotsDropdown"
|
|
5
|
-
class="flex items-center py-2 px-2 text-sm font-medium text-
|
|
5
|
+
class="flex items-center py-2 px-2 text-sm font-medium text-lightThreeDotsMenuIconDots focus:outline-none bg-lightThreeDotsMenuIconBackground rounded border border-lightThreeDotsMenuIconBackgroundBorder hover:bg-lightThreeDotsMenuIconBackgroundHover hover:text-lightThreeDotsMenuIconDotsHover focus:z-10 focus:ring-4 focus:ring-lightThreeDotsMenuIconFocus dark:focus:ring-darkThreeDotsMenuIconFocus dark:bg-darkThreeDotsMenuIconBackground dark:text-darkThreeDotsMenuIconDots dark:border-darkThreeDotsMenuIconBackgroundBorder dark:hover:text-darkThreeDotsMenuIconDotsHover dark:hover:bg-darkThreeDotsMenuIconBackgroundHover rounded-default"
|
|
6
6
|
>
|
|
7
7
|
<svg class="w-3 h-3" aria-hidden="true" xmlns="http://www.w3.org/2000/svg" fill="currentColor" viewBox="0 0 4 15">
|
|
8
8
|
<path d="M3.5 1.5a1.5 1.5 0 1 1-3 0 1.5 1.5 0 0 1 3 0Zm0 6.041a1.5 1.5 0 1 1-3 0 1.5 1.5 0 0 1 3 0Zm0 5.959a1.5 1.5 0 1 1-3 0 1.5 1.5 0 0 1 3 0Z"/>
|
|
@@ -12,10 +12,10 @@
|
|
|
12
12
|
<!-- Dropdown menu -->
|
|
13
13
|
<div
|
|
14
14
|
id="listThreeDotsDropdown"
|
|
15
|
-
class="z-20 hidden bg-
|
|
16
|
-
<ul class="py-2 text-sm text-
|
|
15
|
+
class="z-20 hidden bg-lightThreeDotsMenuBodyBackground divide-y divide-gray-100 rounded-lg shadow w-44 dark:bg-darkThreeDotsMenuBodyBackground dark:divide-gray-600">
|
|
16
|
+
<ul class="py-2 text-sm text-lightThreeDotsMenuBodyText dark:text-darkThreeDotsMenuBodyText" aria-labelledby="dropdownMenuIconButton">
|
|
17
17
|
<li v-for="item in threeDotsDropdownItems" :key="`dropdown-item-${item.label}`">
|
|
18
|
-
<a href="#" class="block px-4 py-2 hover:bg-
|
|
18
|
+
<a href="#" class="block px-4 py-2 hover:bg-lightThreeDotsMenuBodyBackgroundHover hover:text-lightThreeDotsMenuBodyTextHover dark:hover:bg-darkThreeDotsMenuBodyBackgroundHover dark:hover:text-darkThreeDotsMenuBodyTextHover">
|
|
19
19
|
<component :is="getCustomComponent(item)"
|
|
20
20
|
:meta="item.meta"
|
|
21
21
|
:resource="coreStore.resource"
|
|
@@ -24,7 +24,7 @@
|
|
|
24
24
|
</a>
|
|
25
25
|
</li>
|
|
26
26
|
<li v-for="action in customActions" :key="action.id">
|
|
27
|
-
<a href="#" @click.prevent="handleActionClick(action)" class="block px-4 py-2 hover:bg-
|
|
27
|
+
<a href="#" @click.prevent="handleActionClick(action)" class="block px-4 py-2 hover:text-lightThreeDotsMenuBodyTextHover hover:bg-lightThreeDotsMenuBodyBackgroundHover dark:hover:bg-darkThreeDotsMenuBodyBackgroundHover dark:hover:text-darkThreeDotsMenuBodyTextHover">
|
|
28
28
|
<div class="flex items-center gap-2">
|
|
29
29
|
<component
|
|
30
30
|
v-if="action.icon"
|
|
@@ -1,13 +1,8 @@
|
|
|
1
1
|
<template>
|
|
2
2
|
|
|
3
3
|
|
|
4
|
-
<div class="flex items-center w-full p-4
|
|
4
|
+
<div class="flex items-center w-full p-4 rounded-lg shadow-lg dark:text-darkToastText dark:bg-darkToastBackground bg-lightToastBackground text-lightToastText"
|
|
5
5
|
role="alert"
|
|
6
|
-
:class="
|
|
7
|
-
{
|
|
8
|
-
'danger': 'bg-red-100',
|
|
9
|
-
}[toast.variant]
|
|
10
|
-
"
|
|
11
6
|
>
|
|
12
7
|
<div v-if="toast.variant == 'info'" class="inline-flex items-center justify-center flex-shrink-0 w-8 h-8 text-lightPrimary dark:text-darkPrimary bg-lightPrimaryOpacity rounded-lg dark:bg-blue-800 dark:text-blue-200">
|
|
13
8
|
<svg class="w-4 h-4" xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 18 20">
|
|
@@ -36,7 +31,7 @@
|
|
|
36
31
|
|
|
37
32
|
<div class="ms-3 text-sm font-normal max-w-xs pr-2" v-if="toast.messageHtml" v-html="toast.messageHtml"></div>
|
|
38
33
|
<div class="ms-3 text-sm font-normal max-w-xs pr-2" v-else>{{toast.message}}</div>
|
|
39
|
-
<button @click="closeToast" type="button" class="ms-auto -mx-1.5 -my-1.5 bg-
|
|
34
|
+
<button @click="closeToast" type="button" class="ms-auto -mx-1.5 -my-1.5 bg-lightToastCloseIconBackground text-lightToastCloseIcon hover:text-lightToastCloseIconHover rounded-lg focus:ring-2 focus:ring-lightToastCloseIconFocusRing p-1.5 hover:bg-lightToastCloseIconBackgroundHover inline-flex items-center justify-center h-8 w-8 dark:text-darkToastCloseIcon dark:hover:text-darkToastCloseIconHover dark:bg-darkToastCloseIconBackground dark:hover:bg-darkToastCloseIconBackgroundHover dark:focus:ring-darkToastCloseIconFocusRing" >
|
|
40
35
|
<svg class="w-3 h-3" xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 14 14">
|
|
41
36
|
<path stroke="currentColor" stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="m1 1 6 6m0 0 6 6M7 7l6-6M7 7l-6 6"/>
|
|
42
37
|
</svg>
|
|
@@ -27,8 +27,8 @@
|
|
|
27
27
|
</span>
|
|
28
28
|
|
|
29
29
|
<span v-else-if="column.type === 'boolean'">
|
|
30
|
-
<span v-if="record[column.name] === true" class="bg-green-100 whitespace-nowrap text-green-800 text-xs font-medium me-2 px-2.5 py-0.5 rounded dark:bg-gray-700 dark:text-green-400 border border-green-400">{{ $t('Yes') }}</span>
|
|
31
|
-
<span v-else-if="record[column.name] === false" class="bg-red-100 whitespace-nowrap text-red-800 text-xs font-medium me-2 px-2.5 py-0.5 rounded dark:bg-gray-700 dark:text-red-400 border border-red-400">{{ $t('No') }}</span>
|
|
30
|
+
<span v-if="record[column.name] === true" class="af-true-value-icon bg-green-100 whitespace-nowrap text-green-800 text-xs font-medium me-2 px-2.5 py-0.5 rounded dark:bg-gray-700 dark:text-green-400 border border-green-400">{{ $t('Yes') }}</span>
|
|
31
|
+
<span v-else-if="record[column.name] === false" class="af-false-value-icon bg-red-100 whitespace-nowrap text-red-800 text-xs font-medium me-2 px-2.5 py-0.5 rounded dark:bg-gray-700 dark:text-red-400 border border-red-400">{{ $t('No') }}</span>
|
|
32
32
|
<span v-else class="bg-gray-100 whitespace-nowrap text-gray-800 text-xs font-medium me-2 px-2.5 py-0.5 rounded dark:bg-gray-700 dark:text-gray-400 border border-gray-400">{{ $t('Unset') }}</span>
|
|
33
33
|
</span>
|
|
34
34
|
<span
|
|
@@ -39,13 +39,13 @@
|
|
|
39
39
|
<span
|
|
40
40
|
v-if="column.isArray.itemType === 'boolean' && arrayItem"
|
|
41
41
|
:key="`${column.name}-${arrayItemIndex}`"
|
|
42
|
-
class="bg-green-100 whitespace-nowrap text-green-800 text-xs font-medium me-2 px-2.5 py-0.5 rounded dark:bg-gray-700 dark:text-green-400 border border-green-400">
|
|
42
|
+
class="af-true-value-icon bg-green-100 whitespace-nowrap text-green-800 text-xs font-medium me-2 px-2.5 py-0.5 rounded dark:bg-gray-700 dark:text-green-400 border border-green-400">
|
|
43
43
|
{{ $t('Yes') }}
|
|
44
44
|
</span>
|
|
45
45
|
<span
|
|
46
46
|
v-else-if="column.isArray.itemType === 'boolean'"
|
|
47
47
|
:key="`${column.name}-${arrayItemIndex}`"
|
|
48
|
-
class="bg-red-100 whitespace-nowrap text-red-800 text-xs font-medium me-2 px-2.5 py-0.5 rounded dark:bg-gray-700 dark:text-red-400 border border-red-400">
|
|
48
|
+
class="af-false-value-icon bg-red-100 whitespace-nowrap text-red-800 text-xs font-medium me-2 px-2.5 py-0.5 rounded dark:bg-gray-700 dark:text-red-400 border border-red-400">
|
|
49
49
|
{{ $t('No') }}
|
|
50
50
|
</span>
|
|
51
51
|
<span
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
<template>
|
|
2
|
+
<Toggle
|
|
3
|
+
:disabled="readonly"
|
|
4
|
+
@update:modelValue="$emit('update:value', $event)"
|
|
5
|
+
:modelValue="valueFromRecord"
|
|
6
|
+
>
|
|
7
|
+
<p>{{text}}</p>
|
|
8
|
+
</Toggle>
|
|
9
|
+
</template>
|
|
10
|
+
|
|
11
|
+
<script setup lang="ts">
|
|
12
|
+
import Toggle from '@/afcl/Toggle.vue';
|
|
13
|
+
import type {
|
|
14
|
+
AdminForthResourceColumnCommon,
|
|
15
|
+
AdminForthResourceCommon,
|
|
16
|
+
AdminUser,
|
|
17
|
+
} from "@/types/Common";
|
|
18
|
+
|
|
19
|
+
const props = defineProps<{
|
|
20
|
+
value: boolean,
|
|
21
|
+
text: string,
|
|
22
|
+
column: AdminForthResourceColumnCommon,
|
|
23
|
+
record: any,
|
|
24
|
+
meta: any,
|
|
25
|
+
resource: AdminForthResourceCommon,
|
|
26
|
+
adminUser: AdminUser,
|
|
27
|
+
readonly: boolean
|
|
28
|
+
}>();
|
|
29
|
+
console.log(JSON.stringify(props));
|
|
30
|
+
console.log("Current mode:", props.meta?.mode)
|
|
31
|
+
defineEmits(['update:value']);
|
|
32
|
+
const valueFromRecord = props.record[props.column.name]
|
|
33
|
+
const editReadOnly = props.column.editReadonly;
|
|
34
|
+
</script>
|
|
@@ -21,6 +21,7 @@ export type ResourceColumns = {
|
|
|
21
21
|
|
|
22
22
|
export type CoreConfig = {
|
|
23
23
|
brandName: string,
|
|
24
|
+
singleTheme?: 'light' | 'dark',
|
|
24
25
|
brandLogo: string,
|
|
25
26
|
title: string,
|
|
26
27
|
datesFormat: string,
|
|
@@ -33,12 +34,18 @@ export type CoreConfig = {
|
|
|
33
34
|
passwordHashField: string,
|
|
34
35
|
loginBackgroundImage: string,
|
|
35
36
|
loginBackgroundPosition: string,
|
|
37
|
+
removeBackgroundBlendMode: boolean,
|
|
36
38
|
userFullnameField: string,
|
|
37
39
|
},
|
|
38
40
|
emptyFieldPlaceholder?: {
|
|
39
41
|
show?: string,
|
|
40
42
|
list?: string,
|
|
41
43
|
} | string,
|
|
44
|
+
|
|
45
|
+
customHeadItems?: {
|
|
46
|
+
tagName: string;
|
|
47
|
+
attributes: { [key: string]: string | boolean };
|
|
48
|
+
}[],
|
|
42
49
|
}
|
|
43
50
|
|
|
44
51
|
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import type { Express } from 'express';
|
|
1
|
+
import type { Express, Request } from 'express';
|
|
2
2
|
import type { Writable } from 'stream';
|
|
3
3
|
|
|
4
4
|
import { ActionCheckSource, AdminForthFilterOperators, AdminForthSortDirections, AllowedActionsEnum,
|
|
@@ -105,6 +105,23 @@ export interface IExpressHttpServer extends IHttpServer {
|
|
|
105
105
|
authorize(callable: Function): void;
|
|
106
106
|
}
|
|
107
107
|
|
|
108
|
+
export interface ITranslateFunction {
|
|
109
|
+
(
|
|
110
|
+
msg: string,
|
|
111
|
+
category: string,
|
|
112
|
+
params: any,
|
|
113
|
+
pluralizationNumber?: number
|
|
114
|
+
): Promise<string>;
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
// Omit <Request, 'param'> is used to remove 'param' method from Request type for correct docs generation
|
|
118
|
+
export interface IAdminUserExpressRequest extends Omit<Request, 'protocol' | 'param' | 'unshift'> {
|
|
119
|
+
adminUser: AdminUser;
|
|
120
|
+
}
|
|
121
|
+
|
|
122
|
+
export interface ITranslateExpressRequest extends Omit<Request, 'protocol' | 'param' | 'unshift'> {
|
|
123
|
+
tr: ITranslateFunction;
|
|
124
|
+
}
|
|
108
125
|
|
|
109
126
|
export interface IAdminForthSingleFilter {
|
|
110
127
|
field?: string;
|
|
@@ -336,7 +353,7 @@ export interface IAdminForth {
|
|
|
336
353
|
|
|
337
354
|
createResourceRecord(
|
|
338
355
|
params: { resource: AdminForthResource, record: any, adminUser: AdminUser, extra?: HttpExtra }
|
|
339
|
-
): Promise<{ error?: string, createdRecord?: any }>;
|
|
356
|
+
): Promise<{ error?: string, createdRecord?: any, newRecordId?: any }>;
|
|
340
357
|
|
|
341
358
|
updateResourceRecord(
|
|
342
359
|
params: { resource: AdminForthResource, recordId: any, record: any, oldRecord: any, adminUser: AdminUser, extra?: HttpExtra }
|
|
@@ -474,7 +491,7 @@ export type BeforeDataSourceRequestFunction = (params: {
|
|
|
474
491
|
requestUrl: string,
|
|
475
492
|
},
|
|
476
493
|
adminforth: IAdminForth,
|
|
477
|
-
}) => Promise<{ok: boolean, error?: string}>;
|
|
494
|
+
}) => Promise<{ok: boolean, error?: string, newRecordId?: string}>;
|
|
478
495
|
|
|
479
496
|
/**
|
|
480
497
|
* Modify response to change how data is returned after fetching from database.
|
|
@@ -525,7 +542,7 @@ export type BeforeEditSaveFunction = (params: {
|
|
|
525
542
|
oldRecord: any,
|
|
526
543
|
adminforth: IAdminForth,
|
|
527
544
|
extra?: HttpExtra,
|
|
528
|
-
}) => Promise<{ok: boolean, error?: string}>;
|
|
545
|
+
}) => Promise<{ok: boolean, error?: string | null}>;
|
|
529
546
|
|
|
530
547
|
|
|
531
548
|
|
|
@@ -535,7 +552,7 @@ export type BeforeCreateSaveFunction = (params: {
|
|
|
535
552
|
record: any,
|
|
536
553
|
adminforth: IAdminForth,
|
|
537
554
|
extra?: HttpExtra,
|
|
538
|
-
}) => Promise<{ok: boolean, error?: string}>;
|
|
555
|
+
}) => Promise<{ok: boolean, error?: string | null, newRecordId?: string}>;
|
|
539
556
|
|
|
540
557
|
export type AfterCreateSaveFunction = (params: {
|
|
541
558
|
resource: AdminForthResource,
|
|
@@ -619,6 +636,11 @@ interface AdminForthInputConfigCustomization {
|
|
|
619
636
|
*/
|
|
620
637
|
brandName?: string,
|
|
621
638
|
|
|
639
|
+
/**
|
|
640
|
+
* Whether to use single theme for the app
|
|
641
|
+
*/
|
|
642
|
+
singleTheme?: 'light' | 'dark',
|
|
643
|
+
|
|
622
644
|
/**
|
|
623
645
|
* Whether to show brand name in sidebar
|
|
624
646
|
* default is true
|
|
@@ -759,6 +781,16 @@ interface AdminForthInputConfigCustomization {
|
|
|
759
781
|
sidebar?: AdminForthComponentDeclaration | Array<AdminForthComponentDeclaration>,
|
|
760
782
|
everyPageBottom?: AdminForthComponentDeclaration | Array<AdminForthComponentDeclaration>,
|
|
761
783
|
}
|
|
784
|
+
|
|
785
|
+
/**
|
|
786
|
+
* Allows adding custom elements (e.g., <link>, <script>, <meta>) to the <head> of the HTML document.
|
|
787
|
+
* Each item must include a tag name and a set of attributes.
|
|
788
|
+
*/
|
|
789
|
+
customHeadItems?: {
|
|
790
|
+
tagName: string;
|
|
791
|
+
attributes: Record<string, string | boolean>;
|
|
792
|
+
}[];
|
|
793
|
+
|
|
762
794
|
}
|
|
763
795
|
|
|
764
796
|
export interface AdminForthActionInput {
|
|
@@ -935,6 +967,13 @@ export interface AdminForthInputConfig {
|
|
|
935
967
|
*/
|
|
936
968
|
loginBackgroundPosition?: 'over' | '1/2' | '1/3' | '2/3' | '3/4' | '2/5' | '3/5',
|
|
937
969
|
|
|
970
|
+
/**
|
|
971
|
+
* If true, background blend mode will be removed from login background image when position is 'over'
|
|
972
|
+
*
|
|
973
|
+
* Default: false
|
|
974
|
+
*/
|
|
975
|
+
removeBackgroundBlendMode?: boolean,
|
|
976
|
+
|
|
938
977
|
/**
|
|
939
978
|
* Function or functions which will be called before user try to login.
|
|
940
979
|
* Each function will resive User object as an argument
|
|
@@ -956,7 +995,7 @@ export interface AdminForthInputConfig {
|
|
|
956
995
|
/**
|
|
957
996
|
* Any prompt to show users on login. Supports HTML.
|
|
958
997
|
*/
|
|
959
|
-
loginPromptHTML?: string
|
|
998
|
+
loginPromptHTML?: string | (() => string | void | undefined | Promise<string | void | undefined>) | undefined
|
|
960
999
|
|
|
961
1000
|
/**
|
|
962
1001
|
* Remember me days for "Remember Me" checkbox on login page.
|
|
@@ -1064,6 +1103,12 @@ export interface AdminForthConfigCustomization extends Omit<AdminForthInputConfi
|
|
|
1064
1103
|
sidebar: Array<AdminForthComponentDeclarationFull>,
|
|
1065
1104
|
everyPageBottom: Array<AdminForthComponentDeclarationFull>,
|
|
1066
1105
|
},
|
|
1106
|
+
|
|
1107
|
+
customHeadItems?: {
|
|
1108
|
+
tagName: string;
|
|
1109
|
+
attributes: Record<string, string | boolean>;
|
|
1110
|
+
}[];
|
|
1111
|
+
|
|
1067
1112
|
}
|
|
1068
1113
|
|
|
1069
1114
|
export interface AdminForthConfig extends Omit<AdminForthInputConfig, 'customization' | 'resources'> {
|
|
@@ -1318,9 +1363,13 @@ export interface AdminForthResource extends Omit<AdminForthResourceInput, 'optio
|
|
|
1318
1363
|
},
|
|
1319
1364
|
create?: {
|
|
1320
1365
|
/**
|
|
1366
|
+
* Should return `ok: true` to continue saving pipeline and allow creating record in database, and `ok: false` to interrupt pipeline and prevent record creation.
|
|
1367
|
+
* If you need to show error on UI, set `error: \<error message\>` in response.
|
|
1368
|
+
*
|
|
1321
1369
|
* Typical use-cases:
|
|
1322
|
-
* -
|
|
1323
|
-
* -
|
|
1370
|
+
* - Create record by custom code (return `{ ok: false, newRecordId: <id of created record from custom code> }`)
|
|
1371
|
+
* - Validate record before saving to database and interrupt execution if validation failed (return `{ ok: false, error: <validation error> }`), though `allowedActions.create` should be preferred in most cases
|
|
1372
|
+
* - fill-in adminUser as creator of record (set `record.<some field> = x; return \{ ok: true \}`)
|
|
1324
1373
|
* - Attach additional data to record before saving to database (mostly fillOnCreate should be used instead)
|
|
1325
1374
|
*/
|
|
1326
1375
|
beforeSave?: Array<BeforeCreateSaveFunction>,
|
|
@@ -1471,15 +1520,27 @@ export type ShowInInput = ShowInModernInput | ShowInLegacyInput;
|
|
|
1471
1520
|
export type ShowIn = {
|
|
1472
1521
|
[key in AdminForthResourcePages]: AllowedActionValue
|
|
1473
1522
|
}
|
|
1523
|
+
export type BackendOnlyInput =
|
|
1524
|
+
| boolean
|
|
1525
|
+
| ((p: {
|
|
1526
|
+
adminUser: AdminUser;
|
|
1527
|
+
resource: AdminForthResource;
|
|
1528
|
+
meta: any;
|
|
1529
|
+
source: ActionCheckSource;
|
|
1530
|
+
adminforth: IAdminForth;
|
|
1531
|
+
}) => boolean | Promise<boolean>);
|
|
1532
|
+
|
|
1474
1533
|
|
|
1475
|
-
export interface AdminForthResourceColumnInput extends Omit<AdminForthResourceColumnInputCommon, 'showIn'> {
|
|
1534
|
+
export interface AdminForthResourceColumnInput extends Omit<AdminForthResourceColumnInputCommon, 'showIn' | 'backendOnly'> {
|
|
1476
1535
|
showIn?: ShowInInput,
|
|
1477
1536
|
foreignResource?: AdminForthForeignResource,
|
|
1537
|
+
backendOnly?: BackendOnlyInput;
|
|
1478
1538
|
}
|
|
1479
1539
|
|
|
1480
|
-
export interface AdminForthResourceColumn extends Omit<AdminForthResourceColumnCommon, 'showIn'> {
|
|
1540
|
+
export interface AdminForthResourceColumn extends Omit<AdminForthResourceColumnCommon, 'showIn' | 'backendOnly'> {
|
|
1481
1541
|
showIn?: ShowIn,
|
|
1482
1542
|
foreignResource?: AdminForthForeignResource,
|
|
1543
|
+
backendOnly?: BackendOnlyInput;
|
|
1483
1544
|
}
|
|
1484
1545
|
|
|
1485
1546
|
export interface IWebSocketClient {
|
|
@@ -95,12 +95,9 @@ export interface AdminForthBulkActionCommon {
|
|
|
95
95
|
label: string,
|
|
96
96
|
|
|
97
97
|
/**
|
|
98
|
-
*
|
|
99
|
-
* * 'danger' - red button
|
|
100
|
-
* * 'success' - green button
|
|
101
|
-
* * 'active' - blue button
|
|
98
|
+
* Add custom class
|
|
102
99
|
**/
|
|
103
|
-
|
|
100
|
+
buttonCustomCssClass?: string;
|
|
104
101
|
|
|
105
102
|
/**
|
|
106
103
|
* Optional small badge for button which will be displayed in the list view
|
|
@@ -584,6 +581,8 @@ export interface AdminForthForeignResourceCommon {
|
|
|
584
581
|
polymorphicResources?: Array<AdminForthPolymorphicForeignResource>,
|
|
585
582
|
polymorphicOn?: string,
|
|
586
583
|
unsetLabel?: string,
|
|
584
|
+
searchableFields?: string | string[],
|
|
585
|
+
searchIsCaseSensitive?: boolean,
|
|
587
586
|
}
|
|
588
587
|
|
|
589
588
|
export type FillOnCreateFunction = (params: {
|
|
@@ -1055,9 +1054,10 @@ export interface AdminForthConfigForFrontend {
|
|
|
1055
1054
|
usernameFieldName: string,
|
|
1056
1055
|
loginBackgroundImage: string,
|
|
1057
1056
|
loginBackgroundPosition: string,
|
|
1057
|
+
removeBackgroundBlendMode: boolean,
|
|
1058
1058
|
title?: string,
|
|
1059
1059
|
demoCredentials?: string,
|
|
1060
|
-
loginPromptHTML?: string
|
|
1060
|
+
loginPromptHTML?: string | (() => string | Promise<string> | void | Promise<void> | Promise<undefined>) | undefined
|
|
1061
1061
|
loginPageInjections: {
|
|
1062
1062
|
underInputs: Array<AdminForthComponentDeclaration>,
|
|
1063
1063
|
panelHeader: Array<AdminForthComponentDeclaration>,
|
|
@@ -1065,6 +1065,7 @@ export interface AdminForthConfigForFrontend {
|
|
|
1065
1065
|
rememberMeDays: number,
|
|
1066
1066
|
showBrandNameInSidebar: boolean,
|
|
1067
1067
|
brandLogo?: string,
|
|
1068
|
+
singleTheme?: 'light' | 'dark',
|
|
1068
1069
|
datesFormat: string,
|
|
1069
1070
|
timeFormat: string,
|
|
1070
1071
|
auth: any,
|
|
@@ -1080,7 +1081,11 @@ export interface AdminForthConfigForFrontend {
|
|
|
1080
1081
|
header: Array<AdminForthComponentDeclarationFull>,
|
|
1081
1082
|
sidebar: Array<AdminForthComponentDeclarationFull>,
|
|
1082
1083
|
everyPageBottom: Array<AdminForthComponentDeclarationFull>,
|
|
1083
|
-
}
|
|
1084
|
+
},
|
|
1085
|
+
customHeadItems?: {
|
|
1086
|
+
tagName: string;
|
|
1087
|
+
attributes: Record<string, string | boolean>;
|
|
1088
|
+
}[],
|
|
1084
1089
|
}
|
|
1085
1090
|
|
|
1086
1091
|
export interface GetBaseConfigResponse {
|