adminforth 1.3.56-next.9 → 1.3.57-next.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/dist/auth.d.ts.map +1 -1
- package/dist/auth.js +8 -4
- package/dist/auth.js.map +1 -1
- package/dist/basePlugin.d.ts +5 -0
- package/dist/basePlugin.d.ts.map +1 -1
- package/dist/basePlugin.js +6 -1
- package/dist/basePlugin.js.map +1 -1
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +10 -0
- package/dist/index.js.map +1 -1
- package/dist/modules/codeInjector.d.ts.map +1 -1
- package/dist/modules/codeInjector.js +3 -1
- package/dist/modules/codeInjector.js.map +1 -1
- package/dist/modules/configValidator.d.ts.map +1 -1
- package/dist/modules/configValidator.js +15 -2
- package/dist/modules/configValidator.js.map +1 -1
- package/dist/modules/styles.js +2 -2
- package/dist/modules/styles.js.map +1 -1
- package/dist/servers/express.js +2 -1
- package/dist/servers/express.js.map +1 -1
- package/dist/spa/index.html +2 -2
- package/dist/spa/package-lock.json +16 -0
- package/dist/spa/package.json +1 -0
- package/dist/spa/src/App.vue +13 -31
- package/dist/spa/src/components/AfTooltip.vue +43 -0
- package/dist/spa/src/components/BreadcrumbsWithButtons.vue +3 -4
- package/dist/spa/src/components/CustomDatePicker.vue +2 -2
- package/dist/spa/src/components/Dropdown.vue +2 -2
- package/dist/spa/src/components/GroupsTable.vue +170 -0
- package/dist/spa/src/components/ResourceForm.vue +70 -156
- package/dist/spa/src/components/ResourceListTable.vue +123 -124
- package/dist/spa/src/renderers/CompactField.vue +45 -0
- package/dist/spa/src/renderers/CompactUUID.vue +12 -15
- package/dist/spa/src/renderers/CountryFlag.vue +16 -21
- package/dist/spa/src/renderers/HumanNumber.vue +11 -16
- package/dist/spa/src/renderers/RelativeTime.vue +41 -0
- package/dist/spa/src/renderers/URL.vue +18 -0
- package/dist/spa/src/stores/user.ts +0 -5
- package/dist/spa/src/types/AdminForthConfig.ts +25 -11
- package/dist/spa/src/views/CreateView.vue +1 -1
- package/dist/spa/src/views/EditView.vue +1 -1
- package/dist/spa/src/views/ListView.vue +3 -8
- package/dist/spa/src/views/ResourceParent.vue +36 -2
- package/dist/spa/src/views/ShowView.vue +16 -15
- package/dist/types/AdminForthConfig.d.ts +24 -11
- package/dist/types/AdminForthConfig.d.ts.map +1 -1
- package/dist/types/AdminForthConfig.js.map +1 -1
- package/package.json +1 -1
|
@@ -1,22 +1,20 @@
|
|
|
1
1
|
<template>
|
|
2
|
-
<
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
<div :id="`tooltip-${id}`" role="tooltip" v-if="visualValue"
|
|
9
|
-
class="absolute z-10 invisible inline-block px-3 py-2 text-sm font-medium text-white transition-opacity duration-300 bg-gray-900 rounded-lg shadow-sm opacity-0 tooltip dark:bg-gray-700">
|
|
2
|
+
<AfTooltip>
|
|
3
|
+
<span class="flex items-center">
|
|
4
|
+
{{ visualValue }} <IconFileCopyAltSolid @click.stop="copyToCB" class="w-5 h-5 text-lightPrimary dark:text-darkPrimary" v-if="visualValue"/>
|
|
5
|
+
</span>
|
|
6
|
+
<template #tooltip v-if="visualValue">
|
|
10
7
|
{{ props.record[props.column.name] }}
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
8
|
+
</template>
|
|
9
|
+
</AfTooltip>
|
|
10
|
+
|
|
14
11
|
</template>
|
|
15
12
|
|
|
16
13
|
<script setup>
|
|
17
|
-
import { computed, ref, onMounted } from 'vue';
|
|
14
|
+
import { computed, ref, onMounted, nextTick } from 'vue';
|
|
18
15
|
import { IconFileCopyAltSolid } from '@iconify-prerendered/vue-flowbite';
|
|
19
|
-
import
|
|
16
|
+
import AfTooltip from '@/components/AfTooltip.vue';
|
|
17
|
+
|
|
20
18
|
|
|
21
19
|
const visualValue = computed(() => {
|
|
22
20
|
// if lenght is more then 8, show only first 4 and last 4 characters, ... in the middle
|
|
@@ -41,8 +39,7 @@ function copyToCB() {
|
|
|
41
39
|
|
|
42
40
|
onMounted(async () => {
|
|
43
41
|
id.value = Math.random().toString(36).substring(7);
|
|
44
|
-
await
|
|
45
|
-
initFlowbite();
|
|
42
|
+
await nextTick();
|
|
46
43
|
});
|
|
47
44
|
|
|
48
45
|
</script>
|
|
@@ -1,31 +1,25 @@
|
|
|
1
1
|
<template>
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
>
|
|
15
|
-
{{ countryName }}
|
|
16
|
-
<div class="tooltip-arrow" data-popper-arrow></div>
|
|
17
|
-
</div>
|
|
18
|
-
</span>
|
|
19
|
-
|
|
2
|
+
<AfTooltip>
|
|
3
|
+
<span class="flex items-center">
|
|
4
|
+
<span
|
|
5
|
+
:class="{[`fi-${countryIsoLow}`]: true, 'flag-icon': countryName}"
|
|
6
|
+
></span>
|
|
7
|
+
<span v-if="meta.showCountryName" class="ms-2">{{ countryName }}</span>
|
|
8
|
+
</span>
|
|
9
|
+
|
|
10
|
+
<template v-if="shouldShowTooltip" #tooltip>
|
|
11
|
+
{{ countryName }}
|
|
12
|
+
</template>
|
|
13
|
+
</AfTooltip>
|
|
20
14
|
</template>
|
|
21
15
|
|
|
22
16
|
<script setup>
|
|
23
17
|
|
|
24
18
|
import { computed, ref, onMounted } from 'vue';
|
|
25
|
-
import { initFlowbite } from 'flowbite';
|
|
26
19
|
import 'flag-icons/css/flag-icons.min.css';
|
|
27
20
|
import isoCountries from 'i18n-iso-countries';
|
|
28
21
|
import enLocal from 'i18n-iso-countries/langs/en.json';
|
|
22
|
+
import AfTooltip from '@/components/AfTooltip.vue';
|
|
29
23
|
|
|
30
24
|
isoCountries.registerLocale(enLocal);
|
|
31
25
|
|
|
@@ -33,11 +27,12 @@ const props = defineProps(['column', 'record', 'meta', 'resource', 'adminUser'])
|
|
|
33
27
|
|
|
34
28
|
const id = ref();
|
|
35
29
|
|
|
30
|
+
const shouldShowTooltip = computed(() => {
|
|
31
|
+
return !props.meta.showCountryName && countryName.value;
|
|
32
|
+
});
|
|
36
33
|
|
|
37
34
|
onMounted(async () => {
|
|
38
35
|
id.value = Math.random().toString(36).substring(7);
|
|
39
|
-
await new Promise(resolve => setTimeout(resolve, 0));
|
|
40
|
-
initFlowbite();
|
|
41
36
|
});
|
|
42
37
|
|
|
43
38
|
const countryIsoLow = computed(() => {
|
|
@@ -1,19 +1,15 @@
|
|
|
1
1
|
<template>
|
|
2
|
-
<
|
|
3
|
-
:data-tooltip-target="formattedValue && `tooltip-${id}`"
|
|
4
|
-
data-tooltip-placement="left">
|
|
2
|
+
<AfTooltip class="flex items-center" >
|
|
5
3
|
{{ formattedValue }}
|
|
6
|
-
<
|
|
7
|
-
class="absolute z-10 invisible inline-block px-3 py-2 text-sm font-medium text-white transition-opacity duration-300 bg-gray-900 rounded-lg shadow-sm opacity-0 tooltip dark:bg-gray-700">
|
|
4
|
+
<template #tooltip v-if="formattedValue">
|
|
8
5
|
{{ formattedTooltipValue }}
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
</span>
|
|
6
|
+
</template>
|
|
7
|
+
</AfTooltip>
|
|
12
8
|
</template>
|
|
13
9
|
|
|
14
10
|
<script setup>
|
|
15
|
-
import { computed, ref, onMounted } from 'vue';
|
|
16
|
-
import
|
|
11
|
+
import { computed, ref, onMounted, nextTick } from 'vue';
|
|
12
|
+
import AfTooltip from '@/components/AfTooltip.vue';
|
|
17
13
|
|
|
18
14
|
const props = defineProps(['column', 'record']);
|
|
19
15
|
const id = ref();
|
|
@@ -50,13 +46,12 @@
|
|
|
50
46
|
return new Intl.NumberFormat(locale).format(num);
|
|
51
47
|
}
|
|
52
48
|
|
|
53
|
-
function formatNumberUsingIntl(num, locale) {
|
|
54
|
-
|
|
55
|
-
}
|
|
49
|
+
function formatNumberUsingIntl(num, locale) {
|
|
50
|
+
return new Intl.NumberFormat(locale).format(num);
|
|
51
|
+
}
|
|
56
52
|
|
|
57
53
|
onMounted(async () => {
|
|
58
54
|
id.value = Math.random().toString(36).substring(7);
|
|
59
|
-
await
|
|
60
|
-
initFlowbite();
|
|
55
|
+
await nextTick();
|
|
61
56
|
});
|
|
62
|
-
|
|
57
|
+
</script>
|
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
<template>
|
|
2
|
+
<AfTooltip class="flex items-center">
|
|
3
|
+
{{ relativeTime }}
|
|
4
|
+
<template #tooltip v-if="relativeTime">
|
|
5
|
+
{{ fullTime }}
|
|
6
|
+
</template>
|
|
7
|
+
</AfTooltip>
|
|
8
|
+
</template>
|
|
9
|
+
|
|
10
|
+
<script setup>
|
|
11
|
+
import { computed, ref, onMounted } from 'vue';
|
|
12
|
+
import AfTooltip from '@/components/AfTooltip.vue';
|
|
13
|
+
import en from 'javascript-time-ago/locale/en';
|
|
14
|
+
import TimeAgo from 'javascript-time-ago';
|
|
15
|
+
import dayjs from 'dayjs';
|
|
16
|
+
|
|
17
|
+
const id = ref();
|
|
18
|
+
|
|
19
|
+
TimeAgo.addLocale(en);
|
|
20
|
+
|
|
21
|
+
const props = defineProps(['column', 'record']);
|
|
22
|
+
|
|
23
|
+
const userLocale = ref(navigator.language || 'en-US');
|
|
24
|
+
const timeAgoFormatter = new TimeAgo(userLocale.value);
|
|
25
|
+
const relativeTime = computed(() => {
|
|
26
|
+
const value = props.record[props.column.name];
|
|
27
|
+
const date = new Date(value);
|
|
28
|
+
return timeAgoFormatter.format(date);
|
|
29
|
+
});
|
|
30
|
+
|
|
31
|
+
const fullTime = computed(() => {
|
|
32
|
+
const value = props.record[props.column.name];
|
|
33
|
+
const date = dayjs(new Date(value));
|
|
34
|
+
return date.format('DD MMM HH:mm');
|
|
35
|
+
});
|
|
36
|
+
|
|
37
|
+
onMounted(async () => {
|
|
38
|
+
id.value = Math.random().toString(36).substring(7);
|
|
39
|
+
});
|
|
40
|
+
|
|
41
|
+
</script>
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
<template>
|
|
2
|
+
<a v-if='record[column.name]' :href="record[column.name]"
|
|
3
|
+
class="text-blue-500 hover:underline overflow-ellipsis overflow-hidden whitespace-nowrap inline-block max-w-full"
|
|
4
|
+
>{{ record[column.name] }}</a>
|
|
5
|
+
</template>
|
|
6
|
+
|
|
7
|
+
<script setup>
|
|
8
|
+
|
|
9
|
+
defineProps({
|
|
10
|
+
record: Object,
|
|
11
|
+
resource: Object,
|
|
12
|
+
adminUser: Object,
|
|
13
|
+
meta: Object,
|
|
14
|
+
column: Object
|
|
15
|
+
});
|
|
16
|
+
|
|
17
|
+
|
|
18
|
+
</script>
|
|
@@ -1,8 +1,6 @@
|
|
|
1
1
|
import { ref } from 'vue';
|
|
2
2
|
import { defineStore } from 'pinia';
|
|
3
3
|
import { callAdminForthApi } from '@/utils';
|
|
4
|
-
import { initFlowbite } from 'flowbite'
|
|
5
|
-
import { useRouter } from 'vue-router';
|
|
6
4
|
import { useCoreStore } from './core';
|
|
7
5
|
import router from '@/router';
|
|
8
6
|
|
|
@@ -27,9 +25,6 @@ export const useUserStore = defineStore('user', () => {
|
|
|
27
25
|
await router.push('/');
|
|
28
26
|
await router.isReady();
|
|
29
27
|
await coreStore.fetchMenuAndResource();
|
|
30
|
-
setTimeout(() => {
|
|
31
|
-
initFlowbite();
|
|
32
|
-
});
|
|
33
28
|
}
|
|
34
29
|
|
|
35
30
|
async function logout() {
|
|
@@ -916,13 +916,13 @@ export type AdminForthResource = {
|
|
|
916
916
|
hooks?: {
|
|
917
917
|
show?: {
|
|
918
918
|
/**
|
|
919
|
-
* Typical
|
|
919
|
+
* Typical use-cases:
|
|
920
920
|
* - request additional data from database before returning to frontend for soft-join
|
|
921
921
|
*/
|
|
922
922
|
beforeDatasourceRequest?: BeforeDataSourceRequestFunction | Array<BeforeDataSourceRequestFunction>,
|
|
923
923
|
|
|
924
924
|
/**
|
|
925
|
-
* Typical
|
|
925
|
+
* Typical use-cases:
|
|
926
926
|
* - Transform value for some field for record returned from database before returning to frontend (minimize, sanitize, etc)
|
|
927
927
|
* - If some-why you can't use `backendOnly` you can cleanup sensitive fields here
|
|
928
928
|
* - Attach additional data to record before returning to frontend
|
|
@@ -931,21 +931,22 @@ export type AdminForthResource = {
|
|
|
931
931
|
},
|
|
932
932
|
list?: {
|
|
933
933
|
/**
|
|
934
|
-
* Typical
|
|
934
|
+
* Typical use-cases:
|
|
935
935
|
* - add additional filters in addition to what user selected before fetching data from database.
|
|
936
936
|
* - same as hooks.show.beforeDatasourceRequest
|
|
937
937
|
*/
|
|
938
938
|
beforeDatasourceRequest?: BeforeDataSourceRequestFunction | Array<BeforeDataSourceRequestFunction>,
|
|
939
939
|
|
|
940
940
|
/**
|
|
941
|
-
* Typical
|
|
942
|
-
* - Same as hooks.show.afterDatasourceResponse but applied for
|
|
941
|
+
* Typical use-cases:
|
|
942
|
+
* - Same as hooks.show.afterDatasourceResponse but applied for all records returned from database for
|
|
943
|
+
* showing in list view, e.g. add new field to each record in list view
|
|
943
944
|
*/
|
|
944
945
|
afterDatasourceResponse?: AfterDataSourceResponseFunction | Array<AfterDataSourceResponseFunction>,
|
|
945
946
|
},
|
|
946
947
|
create?: {
|
|
947
948
|
/**
|
|
948
|
-
* Typical
|
|
949
|
+
* Typical use-cases:
|
|
949
950
|
* - Validate record before saving to database and interrupt execution if validation failed (`allowedActions.create` should be preferred in most cases)
|
|
950
951
|
* - fill-in adminUser as creator of record
|
|
951
952
|
* - Attach additional data to record before saving to database (mostly fillOnCreate should be used instead)
|
|
@@ -953,32 +954,32 @@ export type AdminForthResource = {
|
|
|
953
954
|
beforeSave?: BeforeSaveFunction | Array<BeforeSaveFunction>,
|
|
954
955
|
|
|
955
956
|
/**
|
|
956
|
-
* Typical
|
|
957
|
+
* Typical use-cases:
|
|
957
958
|
* - Initiate some trigger after record saved to database (e.g sync to another datasource)
|
|
958
959
|
*/
|
|
959
960
|
afterSave?: AfterSaveFunction | Array<AfterSaveFunction>,
|
|
960
961
|
},
|
|
961
962
|
edit?: {
|
|
962
963
|
/**
|
|
963
|
-
* Typical
|
|
964
|
+
* Typical use-cases:
|
|
964
965
|
* - Same as hooks.create.beforeSave but for edit page
|
|
965
966
|
*/
|
|
966
967
|
beforeSave?: BeforeSaveFunction | Array<BeforeSaveFunction>,
|
|
967
968
|
|
|
968
969
|
/**
|
|
969
|
-
* Typical
|
|
970
|
+
* Typical use-cases:
|
|
970
971
|
* - Same as hooks.create.afterSave but for edit page
|
|
971
972
|
*/
|
|
972
973
|
afterSave?: AfterSaveFunction | Array<AfterSaveFunction>,
|
|
973
974
|
},
|
|
974
975
|
delete?: {
|
|
975
976
|
/**
|
|
976
|
-
* Typical
|
|
977
|
+
* Typical use-cases:
|
|
977
978
|
* - Validate that record can be deleted and interrupt execution if validation failed (`allowedActions.delete` should be preferred in most cases)
|
|
978
979
|
*/
|
|
979
980
|
beforeSave?: BeforeSaveFunction | Array<BeforeSaveFunction>,
|
|
980
981
|
/**
|
|
981
|
-
* Typical
|
|
982
|
+
* Typical use-cases:
|
|
982
983
|
* - Initiate some trigger after record deleted from database (e.g sync to another datasource)
|
|
983
984
|
*/
|
|
984
985
|
afterSave?: BeforeSaveFunction | Array<BeforeSaveFunction>,
|
|
@@ -1043,6 +1044,14 @@ export type AdminForthResource = {
|
|
|
1043
1044
|
*/
|
|
1044
1045
|
allowedActions?: AllowedActions,
|
|
1045
1046
|
|
|
1047
|
+
/**
|
|
1048
|
+
* Allows to make groups of columns in create/edit resource pages.
|
|
1049
|
+
*/
|
|
1050
|
+
createEditGroups?: {
|
|
1051
|
+
groupName: string;
|
|
1052
|
+
columns: string[];
|
|
1053
|
+
}[];
|
|
1054
|
+
|
|
1046
1055
|
/**
|
|
1047
1056
|
* Page size for list view
|
|
1048
1057
|
*/
|
|
@@ -1293,6 +1302,11 @@ export type AdminForthConfig = {
|
|
|
1293
1302
|
*/
|
|
1294
1303
|
brandName?: string,
|
|
1295
1304
|
|
|
1305
|
+
/**
|
|
1306
|
+
* Slug which will be used on tech side e.g. to store cookies separately.
|
|
1307
|
+
* Created automatically from brandName if not set.
|
|
1308
|
+
*/
|
|
1309
|
+
_brandNameSlug?: string,
|
|
1296
1310
|
|
|
1297
1311
|
/**
|
|
1298
1312
|
* Whether to show brand name in sidebar
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
<template>
|
|
2
|
-
<div class="relative">
|
|
2
|
+
<div class="relative flex flex-col max-w-full w-full">
|
|
3
3
|
<Teleport to="body">
|
|
4
4
|
<Filters
|
|
5
5
|
:columns="coreStore.resource?.columns"
|
|
@@ -116,11 +116,10 @@ import ResourceListTable from '@/components/ResourceListTable.vue';
|
|
|
116
116
|
import { useCoreStore } from '@/stores/core';
|
|
117
117
|
import { useFiltersStore } from '@/stores/filters';
|
|
118
118
|
import { callAdminForthApi, currentQuery, getIcon, setQuery } from '@/utils';
|
|
119
|
-
import { computed, onMounted, ref, watch } from 'vue';
|
|
119
|
+
import { computed, onMounted, ref, watch, nextTick } from 'vue';
|
|
120
120
|
import { useRoute } from 'vue-router';
|
|
121
121
|
import { showErrorTost } from '@/composables/useFrontendApi'
|
|
122
122
|
import { getCustomComponent, initThreeDotsDropdown } from '@/utils';
|
|
123
|
-
import { initFlowbite } from 'flowbite';
|
|
124
123
|
import ThreeDotsMenu from '@/components/ThreeDotsMenu.vue';
|
|
125
124
|
|
|
126
125
|
|
|
@@ -183,9 +182,7 @@ async function getList() {
|
|
|
183
182
|
return row;
|
|
184
183
|
});
|
|
185
184
|
totalRows.value = data.total;
|
|
186
|
-
await
|
|
187
|
-
initFlowbite(); // for tooltips in table
|
|
188
|
-
|
|
185
|
+
await nextTick();
|
|
189
186
|
}
|
|
190
187
|
|
|
191
188
|
async function startBulkAction(actionId) {
|
|
@@ -246,8 +243,6 @@ async function init() {
|
|
|
246
243
|
resourceId: route.params.resourceId
|
|
247
244
|
});
|
|
248
245
|
|
|
249
|
-
initFlowbite();
|
|
250
|
-
|
|
251
246
|
// !!! clear filters should be in same tick with sort assignment so that watch can catch it as one change
|
|
252
247
|
|
|
253
248
|
// try to init filters from query params
|
|
@@ -1,5 +1,7 @@
|
|
|
1
1
|
<template>
|
|
2
|
-
<div :key="`${$route?.params.resourceId}---${$route?.params.primaryKey}`" class="p-4"
|
|
2
|
+
<div :key="`${$route?.params.resourceId}---${$route?.params.primaryKey}`" class="p-4 flex"
|
|
3
|
+
:class="limitHeightToPage ? 'h-[calc(100vh-3.5rem)]': undefined"
|
|
4
|
+
>
|
|
3
5
|
<RouterView/>
|
|
4
6
|
</div>
|
|
5
7
|
</template>
|
|
@@ -9,9 +11,41 @@
|
|
|
9
11
|
|
|
10
12
|
<script setup>
|
|
11
13
|
|
|
12
|
-
|
|
13
14
|
import { useCoreStore } from '@/stores/core';
|
|
15
|
+
import { computed } from 'vue';
|
|
16
|
+
import { useRoute } from 'vue-router';
|
|
14
17
|
|
|
18
|
+
const route = useRoute()
|
|
15
19
|
const coreStore = useCoreStore()
|
|
16
20
|
|
|
21
|
+
const limitHeightToPage = computed(() => {
|
|
22
|
+
if (route.name !== 'resource-list' ) {
|
|
23
|
+
return false;
|
|
24
|
+
}
|
|
25
|
+
// for mobile phones disable shrinking table at all because of the issues with HEADER and general UX
|
|
26
|
+
// use navigator.userAgent to detect mobile phones
|
|
27
|
+
if (navigator.userAgent.match(/Android/i) || navigator.userAgent.match(/iPhone|iPad|iPod/i)) {
|
|
28
|
+
return false;
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
if (!coreStore.resource?.options?.pageInjections?.list) {
|
|
32
|
+
return true;
|
|
33
|
+
}
|
|
34
|
+
const listPageInjects = coreStore.resource.options.pageInjections.list;
|
|
35
|
+
console.log('asdcoreStore.resource', JSON.stringify(listPageInjects, null, 2))
|
|
36
|
+
|
|
37
|
+
for (const pi of [listPageInjects.beforeBreadcrumbs, listPageInjects.afterBreadcrumbs, listPageInjects.bottom]) {
|
|
38
|
+
if (pi) {
|
|
39
|
+
for (const piItem of pi) {
|
|
40
|
+
if (!piItem.meta?.thinEnoughToShrinkTable) {
|
|
41
|
+
return false;
|
|
42
|
+
}
|
|
43
|
+
}
|
|
44
|
+
}
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
return true;
|
|
48
|
+
|
|
49
|
+
})
|
|
50
|
+
|
|
17
51
|
</script>
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
<template>
|
|
2
|
-
<div class="relative">
|
|
2
|
+
<div class="relative w-full">
|
|
3
3
|
<component
|
|
4
4
|
v-if="!loading"
|
|
5
5
|
v-for="c in coreStore?.resourceOptions?.pageInjections?.show?.beforeBreadcrumbs || []"
|
|
@@ -57,22 +57,23 @@
|
|
|
57
57
|
</div>
|
|
58
58
|
<div
|
|
59
59
|
v-else-if="coreStore.record"
|
|
60
|
-
class="relative overflow-x-auto rounded-default shadow-resourseFormShadow
|
|
60
|
+
class="relative w-full overflow-x-auto rounded-default shadow-resourseFormShadow"
|
|
61
61
|
>
|
|
62
|
-
<table class="w-full text-sm text-left rtl:text-right text-gray-500 dark:text-gray-400
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
</
|
|
62
|
+
<table class="w-full text-sm text-left rtl:text-right text-gray-500 dark:text-gray-400 table-fixed">
|
|
63
|
+
<!-- table fixed layour used to prevent double scroll in inline list plugins -->
|
|
64
|
+
<thead class="text-xs text-gray-700 uppercase dark:text-gray-400 bg-lightFormHeading dark:bg-gray-700 block md:table-row-group ">
|
|
65
|
+
<tr>
|
|
66
|
+
<th scope="col" class="px-6 py-3 hidden md:w-52 md:table-cell">
|
|
67
|
+
Field
|
|
68
|
+
</th>
|
|
69
|
+
<th scope="col" class="px-6 py-3 hidden md:table-cell">
|
|
70
|
+
Value
|
|
71
|
+
</th>
|
|
72
|
+
</tr>
|
|
72
73
|
</thead>
|
|
73
74
|
<tbody>
|
|
74
75
|
<tr v-for="column,i in coreStore.resource?.columns.filter(c => c.showIn.includes('show'))" :key="column.name"
|
|
75
|
-
class="bg-lightForm bg-darkForm odd:dark:bg-gray-900 even:dark:bg-gray-800 dark:border-gray-700"
|
|
76
|
+
class="bg-lightForm bg-darkForm odd:dark:bg-gray-900 even:dark:bg-gray-800 dark:border-gray-700 block md:table-row"
|
|
76
77
|
:class="{ 'border-b': i !== coreStore.resource.columns.filter(c => c.showIn.includes('show')).length - 1 }"
|
|
77
78
|
>
|
|
78
79
|
<component
|
|
@@ -84,10 +85,10 @@
|
|
|
84
85
|
:record="coreStore.record"
|
|
85
86
|
/>
|
|
86
87
|
<template v-else>
|
|
87
|
-
<td class="px-6 py-4
|
|
88
|
+
<td class="px-6 py-4 relative block md:table-cell font-bold md:font-normal pb-0 md:pb-4"> <!--align-top-->
|
|
88
89
|
{{ column.label }}
|
|
89
90
|
</td>
|
|
90
|
-
<td class="px-6 py-4 whitespace-
|
|
91
|
+
<td class="px-6 py-4 whitespace-pre-wrap" :data-af-column="column.name">
|
|
91
92
|
<component
|
|
92
93
|
v-if="column?.components?.show"
|
|
93
94
|
:is="getCustomComponent(column?.components?.show)"
|
|
@@ -883,12 +883,12 @@ export type AdminForthResource = {
|
|
|
883
883
|
hooks?: {
|
|
884
884
|
show?: {
|
|
885
885
|
/**
|
|
886
|
-
* Typical
|
|
886
|
+
* Typical use-cases:
|
|
887
887
|
* - request additional data from database before returning to frontend for soft-join
|
|
888
888
|
*/
|
|
889
889
|
beforeDatasourceRequest?: BeforeDataSourceRequestFunction | Array<BeforeDataSourceRequestFunction>;
|
|
890
890
|
/**
|
|
891
|
-
* Typical
|
|
891
|
+
* Typical use-cases:
|
|
892
892
|
* - Transform value for some field for record returned from database before returning to frontend (minimize, sanitize, etc)
|
|
893
893
|
* - If some-why you can't use `backendOnly` you can cleanup sensitive fields here
|
|
894
894
|
* - Attach additional data to record before returning to frontend
|
|
@@ -897,51 +897,52 @@ export type AdminForthResource = {
|
|
|
897
897
|
};
|
|
898
898
|
list?: {
|
|
899
899
|
/**
|
|
900
|
-
* Typical
|
|
900
|
+
* Typical use-cases:
|
|
901
901
|
* - add additional filters in addition to what user selected before fetching data from database.
|
|
902
902
|
* - same as hooks.show.beforeDatasourceRequest
|
|
903
903
|
*/
|
|
904
904
|
beforeDatasourceRequest?: BeforeDataSourceRequestFunction | Array<BeforeDataSourceRequestFunction>;
|
|
905
905
|
/**
|
|
906
|
-
* Typical
|
|
907
|
-
* - Same as hooks.show.afterDatasourceResponse but applied for
|
|
906
|
+
* Typical use-cases:
|
|
907
|
+
* - Same as hooks.show.afterDatasourceResponse but applied for all records returned from database for
|
|
908
|
+
* showing in list view, e.g. add new field to each record in list view
|
|
908
909
|
*/
|
|
909
910
|
afterDatasourceResponse?: AfterDataSourceResponseFunction | Array<AfterDataSourceResponseFunction>;
|
|
910
911
|
};
|
|
911
912
|
create?: {
|
|
912
913
|
/**
|
|
913
|
-
* Typical
|
|
914
|
+
* Typical use-cases:
|
|
914
915
|
* - Validate record before saving to database and interrupt execution if validation failed (`allowedActions.create` should be preferred in most cases)
|
|
915
916
|
* - fill-in adminUser as creator of record
|
|
916
917
|
* - Attach additional data to record before saving to database (mostly fillOnCreate should be used instead)
|
|
917
918
|
*/
|
|
918
919
|
beforeSave?: BeforeSaveFunction | Array<BeforeSaveFunction>;
|
|
919
920
|
/**
|
|
920
|
-
* Typical
|
|
921
|
+
* Typical use-cases:
|
|
921
922
|
* - Initiate some trigger after record saved to database (e.g sync to another datasource)
|
|
922
923
|
*/
|
|
923
924
|
afterSave?: AfterSaveFunction | Array<AfterSaveFunction>;
|
|
924
925
|
};
|
|
925
926
|
edit?: {
|
|
926
927
|
/**
|
|
927
|
-
* Typical
|
|
928
|
+
* Typical use-cases:
|
|
928
929
|
* - Same as hooks.create.beforeSave but for edit page
|
|
929
930
|
*/
|
|
930
931
|
beforeSave?: BeforeSaveFunction | Array<BeforeSaveFunction>;
|
|
931
932
|
/**
|
|
932
|
-
* Typical
|
|
933
|
+
* Typical use-cases:
|
|
933
934
|
* - Same as hooks.create.afterSave but for edit page
|
|
934
935
|
*/
|
|
935
936
|
afterSave?: AfterSaveFunction | Array<AfterSaveFunction>;
|
|
936
937
|
};
|
|
937
938
|
delete?: {
|
|
938
939
|
/**
|
|
939
|
-
* Typical
|
|
940
|
+
* Typical use-cases:
|
|
940
941
|
* - Validate that record can be deleted and interrupt execution if validation failed (`allowedActions.delete` should be preferred in most cases)
|
|
941
942
|
*/
|
|
942
943
|
beforeSave?: BeforeSaveFunction | Array<BeforeSaveFunction>;
|
|
943
944
|
/**
|
|
944
|
-
* Typical
|
|
945
|
+
* Typical use-cases:
|
|
945
946
|
* - Initiate some trigger after record deleted from database (e.g sync to another datasource)
|
|
946
947
|
*/
|
|
947
948
|
afterSave?: BeforeSaveFunction | Array<BeforeSaveFunction>;
|
|
@@ -999,6 +1000,13 @@ export type AdminForthResource = {
|
|
|
999
1000
|
*
|
|
1000
1001
|
*/
|
|
1001
1002
|
allowedActions?: AllowedActions;
|
|
1003
|
+
/**
|
|
1004
|
+
* Allows to make groups of columns in create/edit resource pages.
|
|
1005
|
+
*/
|
|
1006
|
+
createEditGroups?: {
|
|
1007
|
+
groupName: string;
|
|
1008
|
+
columns: string[];
|
|
1009
|
+
}[];
|
|
1002
1010
|
/**
|
|
1003
1011
|
* Page size for list view
|
|
1004
1012
|
*/
|
|
@@ -1221,6 +1229,11 @@ export type AdminForthConfig = {
|
|
|
1221
1229
|
* Your app name
|
|
1222
1230
|
*/
|
|
1223
1231
|
brandName?: string;
|
|
1232
|
+
/**
|
|
1233
|
+
* Slug which will be used on tech side e.g. to store cookies separately.
|
|
1234
|
+
* Created automatically from brandName if not set.
|
|
1235
|
+
*/
|
|
1236
|
+
_brandNameSlug?: string;
|
|
1224
1237
|
/**
|
|
1225
1238
|
* Whether to show brand name in sidebar
|
|
1226
1239
|
* default is true
|