adminforth 2.4.0-next.2 → 2.4.0-next.200
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/callTsProxy.js +14 -4
- package/commands/cli.js +12 -4
- package/commands/createApp/templates/.env.local.hbs +2 -2
- package/commands/createApp/templates/custom/tsconfig.json.hbs +2 -3
- package/commands/createApp/templates/index.ts.hbs +11 -6
- package/commands/createApp/templates/package.json.hbs +1 -1
- package/commands/createApp/utils.js +40 -7
- package/commands/createCustomComponent/configLoader.js +3 -0
- package/commands/createCustomComponent/configUpdater.js +25 -21
- package/commands/createCustomComponent/fileGenerator.js +1 -1
- package/commands/createCustomComponent/main.js +3 -1
- package/commands/createCustomComponent/templates/customCrud/beforeActionButtons.vue.hbs +38 -0
- package/commands/createCustomComponent/templates/login/beforeLogin.vue.hbs +18 -0
- package/commands/createPlugin/templates/custom/tsconfig.json.hbs +2 -5
- package/commands/createPlugin/templates/package.json.hbs +1 -1
- package/dist/auth.d.ts +9 -1
- package/dist/auth.d.ts.map +1 -1
- package/dist/auth.js +15 -2
- package/dist/auth.js.map +1 -1
- package/dist/dataConnectors/baseConnector.d.ts.map +1 -1
- package/dist/dataConnectors/baseConnector.js +46 -15
- package/dist/dataConnectors/baseConnector.js.map +1 -1
- package/dist/dataConnectors/clickhouse.d.ts.map +1 -1
- package/dist/dataConnectors/clickhouse.js +15 -0
- package/dist/dataConnectors/clickhouse.js.map +1 -1
- package/dist/dataConnectors/mongo.d.ts.map +1 -1
- package/dist/dataConnectors/mongo.js +44 -15
- package/dist/dataConnectors/mongo.js.map +1 -1
- package/dist/dataConnectors/mysql.d.ts.map +1 -1
- package/dist/dataConnectors/mysql.js +11 -0
- package/dist/dataConnectors/mysql.js.map +1 -1
- package/dist/dataConnectors/postgres.d.ts.map +1 -1
- package/dist/dataConnectors/postgres.js +11 -0
- package/dist/dataConnectors/postgres.js.map +1 -1
- package/dist/dataConnectors/sqlite.d.ts.map +1 -1
- package/dist/dataConnectors/sqlite.js +11 -0
- package/dist/dataConnectors/sqlite.js.map +1 -1
- package/dist/index.d.ts +2 -1
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +20 -9
- package/dist/index.js.map +1 -1
- package/dist/modules/codeInjector.d.ts +2 -0
- package/dist/modules/codeInjector.d.ts.map +1 -1
- package/dist/modules/codeInjector.js +55 -12
- package/dist/modules/codeInjector.js.map +1 -1
- package/dist/modules/configValidator.d.ts.map +1 -1
- package/dist/modules/configValidator.js +76 -8
- package/dist/modules/configValidator.js.map +1 -1
- package/dist/modules/restApi.d.ts.map +1 -1
- package/dist/modules/restApi.js +149 -25
- package/dist/modules/restApi.js.map +1 -1
- package/dist/modules/styles.d.ts +485 -13
- package/dist/modules/styles.d.ts.map +1 -1
- package/dist/modules/styles.js +541 -31
- package/dist/modules/styles.js.map +1 -1
- package/dist/modules/utils.d.ts +2 -0
- package/dist/modules/utils.d.ts.map +1 -1
- package/dist/modules/utils.js +16 -0
- package/dist/modules/utils.js.map +1 -1
- package/dist/servers/express.d.ts.map +1 -1
- package/dist/servers/express.js +14 -0
- package/dist/servers/express.js.map +1 -1
- package/dist/spa/index.html +1 -1
- package/dist/spa/package-lock.json +5 -4
- package/dist/spa/package.json +1 -1
- package/dist/spa/src/App.vue +54 -169
- package/dist/spa/src/adminforth.ts +42 -18
- package/dist/spa/src/afcl/BarChart.vue +2 -2
- package/dist/spa/src/afcl/Button.vue +6 -6
- package/dist/spa/src/afcl/ButtonGroup.vue +91 -0
- package/dist/spa/src/afcl/Card.vue +25 -0
- 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 +44 -27
- package/dist/spa/src/afcl/Dropzone.vue +15 -13
- package/dist/spa/src/afcl/Input.vue +9 -7
- package/dist/spa/src/afcl/JsonViewer.vue +25 -0
- package/dist/spa/src/afcl/Link.vue +1 -1
- package/dist/spa/src/afcl/LinkButton.vue +3 -3
- package/dist/spa/src/afcl/PieChart.vue +5 -5
- package/dist/spa/src/afcl/ProgressBar.vue +7 -7
- package/dist/spa/src/afcl/Select.vue +68 -34
- package/dist/spa/src/afcl/Skeleton.vue +6 -6
- package/dist/spa/src/afcl/Table.vue +213 -74
- package/dist/spa/src/afcl/Textarea.vue +31 -0
- package/dist/spa/src/afcl/Toggle.vue +32 -0
- package/dist/spa/src/afcl/Tooltip.vue +2 -3
- package/dist/spa/src/afcl/VerticalTabs.vue +16 -7
- package/dist/spa/src/afcl/index.ts +6 -3
- package/dist/spa/src/components/AcceptModal.vue +7 -7
- package/dist/spa/src/components/Breadcrumbs.vue +5 -5
- package/dist/spa/src/components/ColumnValueInput.vue +38 -18
- package/dist/spa/src/components/ColumnValueInputWrapper.vue +4 -3
- package/dist/spa/src/components/CustomDateRangePicker.vue +9 -8
- package/dist/spa/src/components/CustomRangePicker.vue +37 -8
- package/dist/spa/src/components/ErrorMessage.vue +21 -0
- package/dist/spa/src/components/Filters.vue +85 -39
- package/dist/spa/src/components/GroupsTable.vue +9 -8
- package/dist/spa/src/components/MenuLink.vue +90 -23
- package/dist/spa/src/components/ResourceForm.vue +94 -51
- package/dist/spa/src/components/ResourceListTable.vue +78 -80
- package/dist/spa/src/components/ResourceListTableVirtual.vue +70 -72
- package/dist/spa/src/components/ShowTable.vue +17 -12
- package/dist/spa/src/components/Sidebar.vue +443 -0
- package/dist/spa/src/components/SingleSkeletLoader.vue +6 -6
- package/dist/spa/src/components/SkeleteLoader.vue +3 -3
- package/dist/spa/src/components/ThreeDotsMenu.vue +73 -14
- package/dist/spa/src/components/Toast.vue +27 -9
- package/dist/spa/src/components/UserMenuSettingsButton.vue +68 -0
- package/dist/spa/src/components/ValueRenderer.vue +43 -16
- package/dist/spa/src/controls/BoolToggle.vue +34 -0
- package/dist/spa/src/i18n.ts +1 -1
- package/dist/spa/src/renderers/CompactField.vue +1 -1
- package/dist/spa/src/renderers/CompactUUID.vue +1 -1
- package/dist/spa/src/router/index.ts +8 -0
- package/dist/spa/src/shims-vue.d.ts +5 -0
- package/dist/spa/src/spa_types/core.ts +13 -1
- package/dist/spa/src/stores/core.ts +1 -1
- package/dist/spa/src/stores/filters.ts +29 -2
- package/dist/spa/src/stores/modal.ts +6 -1
- package/dist/spa/src/stores/toast.ts +22 -3
- package/dist/spa/src/types/Back.ts +139 -22
- package/dist/spa/src/types/Common.ts +68 -32
- package/dist/spa/src/types/FrontendAPI.ts +31 -5
- package/dist/spa/src/types/adapters/CaptchaAdapter.ts +34 -0
- package/dist/spa/src/types/adapters/CompletionAdapter.ts +25 -0
- package/dist/spa/src/types/adapters/EmailAdapter.ts +27 -0
- package/dist/spa/src/types/adapters/ImageGenerationAdapter.ts +50 -0
- package/dist/spa/src/types/adapters/ImageVisionAdapter.ts +30 -0
- package/dist/spa/src/types/adapters/KeyValueAdapter.ts +16 -0
- package/dist/spa/src/types/adapters/OAuth2Adapter.ts +34 -0
- package/dist/spa/src/types/adapters/StorageAdapter.ts +73 -0
- package/dist/spa/src/types/adapters/index.ts +8 -0
- package/dist/spa/src/utils.ts +219 -8
- package/dist/spa/src/views/CreateView.vue +18 -19
- package/dist/spa/src/views/EditView.vue +25 -19
- package/dist/spa/src/views/ListView.vue +139 -86
- package/dist/spa/src/views/LoginView.vue +65 -55
- package/dist/spa/src/views/ResourceParent.vue +2 -2
- package/dist/spa/src/views/SettingsView.vue +121 -0
- package/dist/spa/src/views/ShowView.vue +59 -39
- package/dist/spa/src/websocket.ts +6 -1
- package/dist/spa/tsconfig.app.json +1 -1
- package/dist/spa/vite.config.ts +45 -2
- package/dist/types/Back.d.ts +117 -14
- package/dist/types/Back.d.ts.map +1 -1
- package/dist/types/Back.js +15 -0
- package/dist/types/Back.js.map +1 -1
- package/dist/types/Common.d.ts +60 -29
- package/dist/types/Common.d.ts.map +1 -1
- package/dist/types/Common.js.map +1 -1
- package/dist/types/FrontendAPI.d.ts +31 -3
- package/dist/types/FrontendAPI.d.ts.map +1 -1
- package/dist/types/FrontendAPI.js.map +1 -1
- package/dist/types/adapters/CaptchaAdapter.d.ts +30 -0
- package/dist/types/adapters/CaptchaAdapter.d.ts.map +1 -0
- package/dist/types/adapters/CaptchaAdapter.js +5 -0
- package/dist/types/adapters/CaptchaAdapter.js.map +1 -0
- package/dist/types/adapters/CompletionAdapter.d.ts +20 -0
- package/dist/types/adapters/CompletionAdapter.d.ts.map +1 -0
- package/dist/types/adapters/CompletionAdapter.js +2 -0
- package/dist/types/adapters/CompletionAdapter.js.map +1 -0
- package/dist/types/adapters/EmailAdapter.d.ts +20 -0
- package/dist/types/adapters/EmailAdapter.d.ts.map +1 -0
- package/dist/types/adapters/EmailAdapter.js +2 -0
- package/dist/types/adapters/EmailAdapter.js.map +1 -0
- package/dist/types/adapters/ImageGenerationAdapter.d.ts +37 -0
- package/dist/types/adapters/ImageGenerationAdapter.d.ts.map +1 -0
- package/dist/types/adapters/ImageGenerationAdapter.js +2 -0
- package/dist/types/adapters/ImageGenerationAdapter.js.map +1 -0
- package/dist/types/adapters/ImageVisionAdapter.d.ts +25 -0
- package/dist/types/adapters/ImageVisionAdapter.d.ts.map +1 -0
- package/dist/types/adapters/ImageVisionAdapter.js +2 -0
- package/dist/types/adapters/ImageVisionAdapter.js.map +1 -0
- package/dist/types/adapters/KeyValueAdapter.d.ts +10 -0
- package/dist/types/adapters/KeyValueAdapter.d.ts.map +1 -0
- package/dist/types/adapters/KeyValueAdapter.js +2 -0
- package/dist/types/adapters/KeyValueAdapter.js.map +1 -0
- package/dist/types/adapters/OAuth2Adapter.d.ts +32 -0
- package/dist/types/adapters/OAuth2Adapter.d.ts.map +1 -0
- package/dist/types/adapters/OAuth2Adapter.js +2 -0
- package/dist/types/adapters/OAuth2Adapter.js.map +1 -0
- package/dist/types/adapters/StorageAdapter.d.ts +63 -0
- package/dist/types/adapters/StorageAdapter.d.ts.map +1 -0
- package/dist/types/adapters/StorageAdapter.js +2 -0
- package/dist/types/adapters/StorageAdapter.js.map +1 -0
- package/dist/types/adapters/index.d.ts +9 -0
- package/dist/types/adapters/index.d.ts.map +1 -0
- package/dist/types/adapters/index.js +2 -0
- package/dist/types/adapters/index.js.map +1 -0
- package/package.json +3 -2
- package/dist/spa/src/types/Adapters.ts +0 -213
- package/dist/types/Adapters.d.ts +0 -168
- package/dist/types/Adapters.d.ts.map +0 -1
- package/dist/types/Adapters.js +0 -2
- package/dist/types/Adapters.js.map +0 -1
|
@@ -1,10 +1,10 @@
|
|
|
1
1
|
<template>
|
|
2
|
-
<div class="relative flex items-center justify-center min-h-screen bg-
|
|
2
|
+
<div class="relative flex items-center justify-center min-h-screen bg-lightHtml dark:bg-darkHtml w-screen h-screen"
|
|
3
3
|
:style="coreStore.config?.loginBackgroundImage && backgroundPosition === 'over' ? {
|
|
4
4
|
'background-image': 'url(' + loadFile(coreStore.config?.loginBackgroundImage) + ')',
|
|
5
5
|
'background-size': 'cover',
|
|
6
6
|
'background-position': 'center',
|
|
7
|
-
'background-blend-mode': 'darken'
|
|
7
|
+
'background-blend-mode': coreStore.config?.removeBackgroundBlendMode ? 'normal' : 'darken'
|
|
8
8
|
}: {}"
|
|
9
9
|
>
|
|
10
10
|
|
|
@@ -23,44 +23,57 @@
|
|
|
23
23
|
|
|
24
24
|
<!-- Main modal -->
|
|
25
25
|
<div id="authentication-modal" tabindex="-1"
|
|
26
|
-
class="overflow-y-auto flex flex-grow
|
|
26
|
+
class="af-login-modal overflow-y-auto flex flex-grow
|
|
27
27
|
overflow-x-hidden z-50 min-w-[350px] justify-center items-center md:inset-0 h-[calc(100%-1rem)] max-h-full">
|
|
28
28
|
<div class="relative p-4 w-full max-h-full max-w-[400px]">
|
|
29
29
|
<!-- Modal content -->
|
|
30
|
-
<div class="relative bg-
|
|
30
|
+
<div class="af-login-modal-content relative bg-lightLoginViewBackground rounded-lg shadow dark:bg-darkLoginViewBackground dark:shadow-black" >
|
|
31
31
|
<!-- Modal header -->
|
|
32
|
-
<div class="flex items-center justify-between p-4 md:p-5 border-b rounded-t dark:border-gray-600">
|
|
33
|
-
|
|
32
|
+
<div class="af-login-modal-header flex items-center justify-between flex-col p-4 md:p-5 border-b rounded-t dark:border-gray-600">
|
|
33
|
+
|
|
34
|
+
<template v-if="coreStore?.config?.loginPageInjections?.panelHeader.length > 0">
|
|
35
|
+
<component
|
|
36
|
+
v-for="(c, index) in coreStore?.config?.loginPageInjections?.panelHeader || []"
|
|
37
|
+
:key="index"
|
|
38
|
+
:is="getCustomComponent(c)"
|
|
39
|
+
:meta="c.meta"
|
|
40
|
+
/>
|
|
41
|
+
</template>
|
|
42
|
+
<h3 v-else class="text-xl font-semibold text-lightLoginViewText dark:text-darkLoginViewTextColor">
|
|
34
43
|
{{ $t('Sign in to') }} {{ coreStore.config?.brandName }}
|
|
35
44
|
</h3>
|
|
36
45
|
</div>
|
|
37
46
|
<!-- Modal body -->
|
|
38
|
-
<div class="p-4 md:p-5">
|
|
47
|
+
<div class="af-login-modal-body p-4 md:p-5">
|
|
39
48
|
<form class="space-y-4" @submit.prevent>
|
|
40
49
|
<div>
|
|
41
|
-
<label for="username" class="block mb-2 text-sm font-medium text-
|
|
42
|
-
<
|
|
50
|
+
<label for="username" class="block mb-2 text-sm font-medium text-lightLoginViewText dark:text-darkLoginViewTextColor">{{ $t('Your') }} {{ coreStore.config?.usernameFieldName?.toLowerCase() }}</label>
|
|
51
|
+
<Input
|
|
52
|
+
v-model="username"
|
|
43
53
|
autocomplete="username"
|
|
44
54
|
type="username"
|
|
45
55
|
name="username"
|
|
46
56
|
id="username"
|
|
47
57
|
ref="usernameInput"
|
|
48
|
-
oninput="setCustomValidity('')"
|
|
49
58
|
@keydown.enter="passwordInput.focus()"
|
|
50
|
-
class="
|
|
59
|
+
class="w-full"
|
|
60
|
+
placeholder="name@company.com" required />
|
|
51
61
|
</div>
|
|
52
|
-
<div class="
|
|
53
|
-
<label for="password" class="block mb-2 text-sm font-medium text-
|
|
54
|
-
<
|
|
62
|
+
<div class="">
|
|
63
|
+
<label for="password" class="block mb-2 text-sm font-medium text-lightLoginViewText dark:text-darkLoginViewTextColor">{{ $t('Your password') }}</label>
|
|
64
|
+
<Input
|
|
65
|
+
v-model="password"
|
|
55
66
|
ref="passwordInput"
|
|
56
67
|
autocomplete="current-password"
|
|
57
|
-
oninput="setCustomValidity('')"
|
|
58
68
|
@keydown.enter="login"
|
|
59
|
-
:type="!showPw ? 'password': 'text'" name="password" id="password" placeholder="••••••••" class="
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
69
|
+
:type="!showPw ? 'password': 'text'" name="password" id="password" placeholder="••••••••" class="w-full" required>
|
|
70
|
+
<template #rightIcon>
|
|
71
|
+
<button type="button" @click="showPw = !showPw" class="text-lightLoginViewSubTextColor dark:text-darkLoginViewSubTextColor">
|
|
72
|
+
<IconEyeSolid class="w-5 h-5" v-if="!showPw" />
|
|
73
|
+
<IconEyeSlashSolid class="w-5 h-5" v-else />
|
|
74
|
+
</button>
|
|
75
|
+
</template>
|
|
76
|
+
</Input>
|
|
64
77
|
</div>
|
|
65
78
|
|
|
66
79
|
<div v-if="coreStore.config.rememberMeDays"
|
|
@@ -77,28 +90,21 @@
|
|
|
77
90
|
v-for="c in coreStore?.config?.loginPageInjections?.underInputs || []"
|
|
78
91
|
:is="getCustomComponent(c)"
|
|
79
92
|
:meta="c.meta"
|
|
93
|
+
@update:disableLoginButton="setDisableLoginButton($event)"
|
|
80
94
|
/>
|
|
81
95
|
|
|
82
|
-
<
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
<span class="sr-only">{{ $t('Info') }}</span>
|
|
87
|
-
<div>
|
|
88
|
-
{{ error }}
|
|
89
|
-
</div>
|
|
90
|
-
</div>
|
|
91
|
-
|
|
92
|
-
<div v-if="coreStore.config?.loginPromptHTML"
|
|
93
|
-
class="flex items-center p-4 mb-4 text-sm text-gray-800 rounded-lg bg-gray-50 dark:bg-gray-800 dark:text-gray-400" role="alert"
|
|
96
|
+
<ErrorMessage :error="error" />
|
|
97
|
+
|
|
98
|
+
<div v-if="loginPromptHTML"
|
|
99
|
+
class="flex items-center p-4 mb-4 text-sm text-lightLoginViewPromptText rounded-lg bg-lightLoginViewPromptBackground dark:bg-darkLoginViewPromptBackground dark:text-darkLoginViewPromptText" role="alert"
|
|
94
100
|
>
|
|
95
101
|
<svg class="flex-shrink-0 inline w-4 h-4 me-3" aria-hidden="true" xmlns="http://www.w3.org/2000/svg" fill="currentColor" viewBox="0 0 20 20">
|
|
96
102
|
<path d="M10 .5a9.5 9.5 0 1 0 9.5 9.5A9.51 9.51 0 0 0 10 .5ZM9.5 4a1.5 1.5 0 1 1 0 3 1.5 1.5 0 0 1 0-3ZM12 15H8a1 1 0 0 1 0-2h1v-3H8a1 1 0 0 1 0-2h2a1 1 0 0 1 1 1v4h1a1 1 0 0 1 0 2Z"/>
|
|
97
103
|
</svg>
|
|
98
104
|
<span class="sr-only">{{ $t('Info') }}</span>
|
|
99
|
-
<div v-html="
|
|
105
|
+
<div v-html="loginPromptHTML"></div>
|
|
100
106
|
</div>
|
|
101
|
-
<Button @click="login" :loader="inProgress" :disabled="inProgress" class="w-full">
|
|
107
|
+
<Button @click="login" :loader="inProgress" :disabled="inProgress || disableLoginButton" class="w-full">
|
|
102
108
|
{{ $t('Login to your account') }}
|
|
103
109
|
</Button>
|
|
104
110
|
</form>
|
|
@@ -112,7 +118,7 @@
|
|
|
112
118
|
</template>
|
|
113
119
|
|
|
114
120
|
|
|
115
|
-
<script setup>
|
|
121
|
+
<script setup lang="ts">
|
|
116
122
|
|
|
117
123
|
import { getCustomComponent } from '@/utils';
|
|
118
124
|
import { onBeforeMount, onMounted, ref, computed } from 'vue';
|
|
@@ -121,30 +127,43 @@ import { useUserStore } from '@/stores/user';
|
|
|
121
127
|
import { IconEyeSolid, IconEyeSlashSolid } from '@iconify-prerendered/vue-flowbite';
|
|
122
128
|
import { callAdminForthApi, loadFile } from '@/utils';
|
|
123
129
|
import { useRoute, useRouter } from 'vue-router';
|
|
124
|
-
import { Button, Checkbox } from '@/afcl';
|
|
130
|
+
import { Button, Checkbox, Input } from '@/afcl';
|
|
125
131
|
import { useI18n } from 'vue-i18n';
|
|
132
|
+
import ErrorMessage from '@/components/ErrorMessage.vue';
|
|
126
133
|
|
|
127
134
|
const { t } = useI18n();
|
|
128
135
|
|
|
129
136
|
const passwordInput = ref(null);
|
|
130
137
|
const usernameInput = ref(null);
|
|
131
138
|
const rememberMeValue= ref(false);
|
|
139
|
+
const username = ref('');
|
|
140
|
+
const password = ref('');
|
|
132
141
|
|
|
133
142
|
const route = useRoute();
|
|
134
143
|
const router = useRouter();
|
|
135
144
|
const inProgress = ref(false);
|
|
136
|
-
|
|
145
|
+
const loginPromptHTML = ref()
|
|
137
146
|
const coreStore = useCoreStore();
|
|
138
147
|
const user = useUserStore();
|
|
139
148
|
|
|
140
149
|
const showPw = ref(false);
|
|
141
150
|
|
|
142
151
|
const error = ref(null);
|
|
152
|
+
const disableLoginButton = ref(false);
|
|
143
153
|
|
|
144
154
|
const backgroundPosition = computed(() => {
|
|
145
155
|
return coreStore.config?.loginBackgroundPosition || '1/2';
|
|
146
156
|
});
|
|
147
157
|
|
|
158
|
+
|
|
159
|
+
async function getLoginFormConfig() {
|
|
160
|
+
const response = await callAdminForthApi({
|
|
161
|
+
path: '/get_login_form_config',
|
|
162
|
+
method: 'GET',
|
|
163
|
+
});
|
|
164
|
+
loginPromptHTML.value = response.loginPromptHTML;
|
|
165
|
+
}
|
|
166
|
+
|
|
148
167
|
onBeforeMount(() => {
|
|
149
168
|
if (localStorage.getItem('isAuthorized') === 'true') {
|
|
150
169
|
// if route has next param, redirect
|
|
@@ -158,29 +177,17 @@ onBeforeMount(() => {
|
|
|
158
177
|
})
|
|
159
178
|
|
|
160
179
|
onMounted(async () => {
|
|
180
|
+
getLoginFormConfig();
|
|
161
181
|
if (coreStore.config?.demoCredentials) {
|
|
162
|
-
const [
|
|
163
|
-
|
|
164
|
-
|
|
182
|
+
const [demoUsername, demoPassword] = coreStore.config.demoCredentials.split(':');
|
|
183
|
+
username.value = demoUsername;
|
|
184
|
+
password.value = demoPassword;
|
|
165
185
|
}
|
|
166
186
|
usernameInput.value.focus();
|
|
167
187
|
});
|
|
168
188
|
|
|
169
189
|
|
|
170
190
|
async function login() {
|
|
171
|
-
|
|
172
|
-
const username = usernameInput.value.value;
|
|
173
|
-
const password = passwordInput.value.value;
|
|
174
|
-
|
|
175
|
-
if (!username) {
|
|
176
|
-
usernameInput.value.setCustomValidity(t('Please fill out this field.'));
|
|
177
|
-
return;
|
|
178
|
-
}
|
|
179
|
-
if (!password) {
|
|
180
|
-
passwordInput.value.setCustomValidity(t('Please fill out this field.'));
|
|
181
|
-
return;
|
|
182
|
-
}
|
|
183
|
-
|
|
184
191
|
if (inProgress.value) {
|
|
185
192
|
return;
|
|
186
193
|
}
|
|
@@ -189,8 +196,8 @@ async function login() {
|
|
|
189
196
|
path: '/login',
|
|
190
197
|
method: 'POST',
|
|
191
198
|
body: {
|
|
192
|
-
username,
|
|
193
|
-
password,
|
|
199
|
+
username: username.value,
|
|
200
|
+
password: password.value,
|
|
194
201
|
rememberMe: rememberMeValue.value,
|
|
195
202
|
}
|
|
196
203
|
});
|
|
@@ -206,5 +213,8 @@ async function login() {
|
|
|
206
213
|
|
|
207
214
|
}
|
|
208
215
|
|
|
216
|
+
function setDisableLoginButton(value: boolean) {
|
|
217
|
+
disableLoginButton.value = value;
|
|
218
|
+
}
|
|
209
219
|
|
|
210
220
|
</script>
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
<template>
|
|
2
|
-
<div :key="`${$route?.params.resourceId}---${$route?.params.primaryKey}`" class="p-4 flex"
|
|
2
|
+
<div :key="`${$route?.params.resourceId}---${$route?.params.primaryKey}`" class="af-resource-parent p-4 flex"
|
|
3
3
|
:class="limitHeightToPage ? 'h-[calc(100dvh-3.5rem)]': undefined"
|
|
4
4
|
>
|
|
5
5
|
<RouterView/>
|
|
@@ -33,7 +33,7 @@ const limitHeightToPage = computed(() => {
|
|
|
33
33
|
}
|
|
34
34
|
const listPageInjects = coreStore.resource.options.pageInjections.list;
|
|
35
35
|
|
|
36
|
-
for (const pi of [listPageInjects.beforeBreadcrumbs, listPageInjects.afterBreadcrumbs, listPageInjects.bottom]) {
|
|
36
|
+
for (const pi of [listPageInjects.beforeBreadcrumbs, listPageInjects.beforeActionButtons, listPageInjects.afterBreadcrumbs, listPageInjects.bottom]) {
|
|
37
37
|
if (pi) {
|
|
38
38
|
for (const piItem of pi) {
|
|
39
39
|
if (!piItem.meta?.thinEnoughToShrinkTable) {
|
|
@@ -0,0 +1,121 @@
|
|
|
1
|
+
<template>
|
|
2
|
+
<div class="mt-20 h-full w-full" :class="{ 'hidden': initialTabSet === false }">
|
|
3
|
+
<div v-if="!coreStore?.config?.settingPages || coreStore?.config?.settingPages.length === 0">
|
|
4
|
+
<p>No setting pages configured or still loading...</p>
|
|
5
|
+
</div>
|
|
6
|
+
<VerticalTabs v-else ref="VerticalTabsRef" v-model:active-tab="activeTab" @update:active-tab="setURL({slug: $event, pageLabel: ''})">
|
|
7
|
+
<template v-for="(c,i) in coreStore?.config?.settingPages" :key="`tab:${settingPageSlotName(c,i)}`" v-slot:['tab:'+c.slug]>
|
|
8
|
+
<div class="flex items-center justify-center whitespace-nowrap w-full px-4 gap-2" @click="setURL(c)">
|
|
9
|
+
<component v-if="c.icon" :is="getIcon(c.icon)" class="w-5 h-5 group-hover:text-lightSidebarIconsHover transition duration-75 dark:group-hover:text-darkSidebarIconsHover dark:text-darkSidebarIcons" ></component>
|
|
10
|
+
{{ c.pageLabel }}
|
|
11
|
+
</div>
|
|
12
|
+
</template>
|
|
13
|
+
|
|
14
|
+
<template v-for="(c,i) in coreStore?.config?.settingPages" :key="`${settingPageSlotName(c,i)}-content`" v-slot:[c.slug]>
|
|
15
|
+
<component
|
|
16
|
+
:is="getCustomComponent({file: c.component || ''})"
|
|
17
|
+
:resource="coreStore.resource"
|
|
18
|
+
:adminUser="coreStore.adminUser"
|
|
19
|
+
/>
|
|
20
|
+
</template>
|
|
21
|
+
</VerticalTabs>
|
|
22
|
+
</div>
|
|
23
|
+
</template>
|
|
24
|
+
|
|
25
|
+
<script setup lang="ts">
|
|
26
|
+
import { ref, onMounted, watch } from 'vue';
|
|
27
|
+
import { useRouter } from 'vue-router';
|
|
28
|
+
import { useCoreStore } from '@/stores/core';
|
|
29
|
+
import { getCustomComponent, getIcon } from '@/utils';
|
|
30
|
+
import { Dropdown } from 'flowbite';
|
|
31
|
+
import adminforth from '@/adminforth';
|
|
32
|
+
import { VerticalTabs } from '@/afcl'
|
|
33
|
+
import { useRoute } from 'vue-router'
|
|
34
|
+
|
|
35
|
+
const route = useRoute()
|
|
36
|
+
const coreStore = useCoreStore();
|
|
37
|
+
const router = useRouter();
|
|
38
|
+
|
|
39
|
+
const routerIsReady = ref(false);
|
|
40
|
+
const loginRedirectCheckIsReady = ref(false);
|
|
41
|
+
const dropdownUserButton = ref<HTMLElement | null>(null);
|
|
42
|
+
const VerticalTabsRef = ref();
|
|
43
|
+
const activeTab = ref('');
|
|
44
|
+
const initialTabSet = ref(false);
|
|
45
|
+
|
|
46
|
+
watch(() => route?.params?.page, (val) => {
|
|
47
|
+
handleURLChange(val as string | null);
|
|
48
|
+
});
|
|
49
|
+
|
|
50
|
+
async function initRouter() {
|
|
51
|
+
await router.isReady();
|
|
52
|
+
routerIsReady.value = true;
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
watch(dropdownUserButton, (el) => {
|
|
56
|
+
if (el) {
|
|
57
|
+
const dd = new Dropdown(
|
|
58
|
+
document.querySelector('#dropdown-user') as HTMLElement,
|
|
59
|
+
document.querySelector('[data-dropdown-toggle="dropdown-user"]') as HTMLElement,
|
|
60
|
+
);
|
|
61
|
+
adminforth.closeUserMenuDropdown = () => dd.hide();
|
|
62
|
+
}
|
|
63
|
+
});
|
|
64
|
+
|
|
65
|
+
onMounted(async () => {
|
|
66
|
+
if (coreStore.adminUser) {
|
|
67
|
+
await loadMenu();
|
|
68
|
+
loginRedirectCheckIsReady.value = true;
|
|
69
|
+
const routeParamsPage = route?.params?.page;
|
|
70
|
+
if (!routeParamsPage) {
|
|
71
|
+
if (coreStore.config?.settingPages?.[0]) {
|
|
72
|
+
setURL(coreStore.config.settingPages[0]);
|
|
73
|
+
}
|
|
74
|
+
} else {
|
|
75
|
+
handleURLChange(routeParamsPage as string | null);
|
|
76
|
+
}
|
|
77
|
+
}
|
|
78
|
+
});
|
|
79
|
+
|
|
80
|
+
async function loadMenu() {
|
|
81
|
+
await initRouter();
|
|
82
|
+
await coreStore.fetchMenuAndResource();
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
function slugifyString(str: string): string {
|
|
86
|
+
return str
|
|
87
|
+
.toString()
|
|
88
|
+
.toLowerCase()
|
|
89
|
+
.replace(/\s+/g, '-')
|
|
90
|
+
.replace(/[^a-z0-9-_]/g, '-');
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
function setURL(item: {
|
|
94
|
+
pageLabel: string;
|
|
95
|
+
slug?: string | undefined;
|
|
96
|
+
}) {
|
|
97
|
+
router.replace({
|
|
98
|
+
name: 'settings',
|
|
99
|
+
params: { page: item?.slug }
|
|
100
|
+
});
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
function handleURLChange(val: string | null) {
|
|
104
|
+
let isParamInTabs;
|
|
105
|
+
for (const c of coreStore?.config?.settingPages || []) {
|
|
106
|
+
if (c.slug ? c.slug === val : slugifyString(c.pageLabel) === val) {
|
|
107
|
+
isParamInTabs = true;
|
|
108
|
+
break;
|
|
109
|
+
}
|
|
110
|
+
}
|
|
111
|
+
if (isParamInTabs) {
|
|
112
|
+
VerticalTabsRef.value.setActiveTab(val);
|
|
113
|
+
activeTab.value = val as string;
|
|
114
|
+
if (!initialTabSet.value) initialTabSet.value = true;
|
|
115
|
+
} else {
|
|
116
|
+
if (coreStore.config?.settingPages?.[0]) {
|
|
117
|
+
setURL(coreStore.config.settingPages[0]);
|
|
118
|
+
}
|
|
119
|
+
}
|
|
120
|
+
}
|
|
121
|
+
</script>
|
|
@@ -4,7 +4,7 @@
|
|
|
4
4
|
v-if="!loading"
|
|
5
5
|
v-for="c in coreStore?.resourceOptions?.pageInjections?.show?.beforeBreadcrumbs || []"
|
|
6
6
|
:is="getCustomComponent(c)"
|
|
7
|
-
:meta="c.meta"
|
|
7
|
+
:meta="(c as AdminForthComponentDeclarationFull).meta"
|
|
8
8
|
:record="coreStore.record"
|
|
9
9
|
:resource="coreStore.resource"
|
|
10
10
|
:adminUser="coreStore.adminUser"
|
|
@@ -15,7 +15,7 @@
|
|
|
15
15
|
v-for="action in coreStore.resource.options.actions.filter(a => a.showIn?.showButton)"
|
|
16
16
|
:key="action.id"
|
|
17
17
|
@click="startCustomAction(action.id)"
|
|
18
|
-
:disabled="actionLoadingStates[action.id]"
|
|
18
|
+
:disabled="actionLoadingStates[action.id!]"
|
|
19
19
|
class="flex items-center py-1 px-3 text-sm font-medium text-gray-900 focus:outline-none bg-white rounded-default border border-gray-300 hover:bg-gray-100 hover:text-lightPrimary focus:z-10 focus:ring-4 focus:ring-gray-100 dark:focus:ring-gray-700 dark:bg-gray-800 dark:text-gray-400 dark:border-gray-600 dark:hover:text-white dark:hover:bg-gray-700"
|
|
20
20
|
>
|
|
21
21
|
<component
|
|
@@ -28,28 +28,28 @@
|
|
|
28
28
|
</template>
|
|
29
29
|
<RouterLink v-if="coreStore.resource?.options?.allowedActions?.create"
|
|
30
30
|
:to="{ name: 'resource-create', params: { resourceId: $route.params.resourceId } }"
|
|
31
|
-
class="flex items-center py-1 px-3 text-sm font-medium text-
|
|
31
|
+
class="af-add-new-button flex items-center py-1 px-3 text-sm font-medium text-lightShowViewButtonText focus:outline-none bg-lightShowViewButtonBackground rounded border border-lightShowViewButtonBorder hover:bg-lightShowViewButtonBackgroundHover hover:text-lightShowViewButtonTextHover focus:z-10 focus:ring-4 focus:ring-lightShowViewButtonFocusRing dark:focus:ring-darkShowViewButtonFocusRing dark:bg-darkShowViewButtonBackground dark:text-darkShowViewButtonText dark:border-darkShowViewButtonBorder dark:hover:text-darkShowViewButtonTextHover dark:hover:bg-darkShowViewButtonBackgroundHover rounded-default gap-1"
|
|
32
32
|
>
|
|
33
|
-
<IconPlusOutline class="w-4 h-4
|
|
33
|
+
<IconPlusOutline class="w-4 h-4"/>
|
|
34
34
|
{{ $t('Add new') }}
|
|
35
35
|
</RouterLink>
|
|
36
36
|
|
|
37
37
|
<RouterLink v-if="coreStore?.resourceOptions?.allowedActions?.edit" :to="{ name: 'resource-edit', params: { resourceId: $route.params.resourceId, primaryKey: $route.params.primaryKey } }"
|
|
38
|
-
class="flex items-center py-1 px-3 text-sm font-medium text-
|
|
38
|
+
class="flex items-center af-edit-button py-1 px-3 text-sm font-medium text-lightShowViewButtonText focus:outline-none bg-lightShowViewButtonBackground rounded-default border border-lightShowViewButtonBorder hover:bg-lightShowViewButtonBackgroundHover hover:text-lightShowViewButtonTextHover focus:z-10 focus:ring-4 focus:ring-lightShowViewButtonFocusRing dark:focus:ring-darkShowViewButtonFocusRing dark:bg-darkShowViewButtonBackground dark:text-darkShowViewButtonText dark:border-darkShowViewButtonBorder dark:hover:text-darkShowViewButtonTextHover dark:hover:bg-darkShowViewButtonBackgroundHover gap-1"
|
|
39
39
|
>
|
|
40
40
|
<IconPenSolid class="w-4 h-4" />
|
|
41
41
|
{{ $t('Edit') }}
|
|
42
42
|
</RouterLink>
|
|
43
43
|
|
|
44
44
|
<button v-if="coreStore?.resourceOptions?.allowedActions?.delete" @click="deleteRecord"
|
|
45
|
-
class="flex items-center py-1 px-3 text-sm font-medium rounded-default text-red-600 focus:outline-none bg-
|
|
45
|
+
class="flex items-center af-delete-button py-1 px-3 text-sm font-medium rounded-default text-red-600 focus:outline-none bg-lightShowViewButtonBackground border border-lightShowViewButtonBorder hover:bg-lightShowViewButtonBackgroundHover hover:text-red-700 focus:z-10 focus:ring-4 focus:ring-lightShowViewButtonFocusRing dark:focus:ring-darkShowViewButtonFocusRing dark:bg-darkShowViewButtonBackground dark:text-red-500 dark:border-darkShowViewButtonBorder dark:hover:text-darkShowViewButtonTextHover dark:hover:bg-darkShowViewButtonBackgroundHover gap-1"
|
|
46
46
|
>
|
|
47
47
|
<IconTrashBinSolid class="w-4 h-4" />
|
|
48
48
|
{{ $t('Delete') }}
|
|
49
49
|
</button>
|
|
50
50
|
|
|
51
51
|
<ThreeDotsMenu
|
|
52
|
-
:threeDotsDropdownItems="coreStore.resourceOptions?.pageInjections?.show?.threeDotsDropdownItems"
|
|
52
|
+
:threeDotsDropdownItems="(coreStore.resourceOptions?.pageInjections?.show?.threeDotsDropdownItems as [])"
|
|
53
53
|
:customActions="customActions"
|
|
54
54
|
></ThreeDotsMenu>
|
|
55
55
|
</BreadcrumbsWithButtons>
|
|
@@ -57,7 +57,7 @@
|
|
|
57
57
|
<component
|
|
58
58
|
v-for="c in coreStore?.resourceOptions?.pageInjections?.show?.afterBreadcrumbs || []"
|
|
59
59
|
:is="getCustomComponent(c)"
|
|
60
|
-
:meta="c.meta"
|
|
60
|
+
:meta="(c as AdminForthComponentDeclarationFull).meta"
|
|
61
61
|
:record="coreStore.record"
|
|
62
62
|
:resource="coreStore.resource"
|
|
63
63
|
:adminUser="coreStore.adminUser"
|
|
@@ -76,11 +76,11 @@
|
|
|
76
76
|
v-else-if="coreStore.record"
|
|
77
77
|
class="relative w-full flex flex-col gap-4"
|
|
78
78
|
>
|
|
79
|
-
<div v-if="!groups.length && allColumns
|
|
79
|
+
<div v-if="!groups.length && allColumns?.length">
|
|
80
80
|
<ShowTable
|
|
81
|
-
:columns="allColumns"
|
|
82
81
|
:resource="coreStore.resource"
|
|
83
82
|
:record="coreStore.record"
|
|
83
|
+
:columns="allColumns as Array<{ name: string; label?: string; components?: any }>"
|
|
84
84
|
/>
|
|
85
85
|
</div>
|
|
86
86
|
<template v-else>
|
|
@@ -93,12 +93,12 @@
|
|
|
93
93
|
:record="coreStore.record"
|
|
94
94
|
/>
|
|
95
95
|
</template>
|
|
96
|
-
<template v-if="otherColumns.length > 0">
|
|
96
|
+
<template v-if="otherColumns && otherColumns.length > 0">
|
|
97
97
|
<ShowTable
|
|
98
|
-
:columns="otherColumns"
|
|
99
98
|
groupName="Other Fields"
|
|
100
99
|
:resource="coreStore.resource"
|
|
101
100
|
:record="coreStore.record"
|
|
101
|
+
:columns="otherColumns as Array<{ name: string; label?: string; components?: any }>"
|
|
102
102
|
/>
|
|
103
103
|
</template>
|
|
104
104
|
</template>
|
|
@@ -112,8 +112,7 @@
|
|
|
112
112
|
v-if="!loading"
|
|
113
113
|
v-for="c in coreStore?.resourceOptions?.pageInjections?.show?.bottom || []"
|
|
114
114
|
:is="getCustomComponent(c)"
|
|
115
|
-
:meta="c.meta"
|
|
116
|
-
:column="column"
|
|
115
|
+
:meta="(c as AdminForthComponentDeclarationFull).meta"
|
|
117
116
|
:record="coreStore.record"
|
|
118
117
|
:resource="coreStore.resource"
|
|
119
118
|
:adminUser="coreStore.adminUser"
|
|
@@ -140,6 +139,7 @@ import ShowTable from '@/components/ShowTable.vue';
|
|
|
140
139
|
import adminforth from "@/adminforth";
|
|
141
140
|
import { useI18n } from 'vue-i18n';
|
|
142
141
|
import { getIcon } from '@/utils';
|
|
142
|
+
import { type AdminForthComponentDeclarationFull, type AdminForthResourceColumnCommon, type FieldGroup } from '@/types/Common.js';
|
|
143
143
|
|
|
144
144
|
const route = useRoute();
|
|
145
145
|
const router = useRouter();
|
|
@@ -147,62 +147,65 @@ const loading = ref(true);
|
|
|
147
147
|
const { t } = useI18n();
|
|
148
148
|
const coreStore = useCoreStore();
|
|
149
149
|
|
|
150
|
-
const actionLoadingStates = ref({});
|
|
150
|
+
const actionLoadingStates = ref<Record<string, boolean>>({});
|
|
151
151
|
|
|
152
152
|
const customActions = computed(() => {
|
|
153
|
-
return coreStore.resource?.options?.actions?.filter(a => a.showIn?.showThreeDotsMenu) || [];
|
|
153
|
+
return coreStore.resource?.options?.actions?.filter((a: any) => a.showIn?.showThreeDotsMenu) || [];
|
|
154
154
|
});
|
|
155
155
|
|
|
156
156
|
onMounted(async () => {
|
|
157
157
|
loading.value = true;
|
|
158
158
|
await coreStore.fetchResourceFull({
|
|
159
|
-
resourceId: route.params.resourceId
|
|
159
|
+
resourceId: route.params.resourceId as string,
|
|
160
160
|
});
|
|
161
161
|
initThreeDotsDropdown();
|
|
162
162
|
await coreStore.fetchRecord({
|
|
163
|
-
resourceId: route.params.resourceId,
|
|
164
|
-
primaryKey: route.params.primaryKey,
|
|
163
|
+
resourceId: route.params.resourceId as string,
|
|
164
|
+
primaryKey: route.params.primaryKey as string,
|
|
165
165
|
source: 'show',
|
|
166
166
|
});
|
|
167
|
-
|
|
167
|
+
if(coreStore.resourceOptions){
|
|
168
|
+
checkAcessByAllowedActions(coreStore.resourceOptions.allowedActions,'show');
|
|
169
|
+
}
|
|
168
170
|
loading.value = false;
|
|
169
171
|
});
|
|
170
172
|
|
|
171
173
|
const groups = computed(() => {
|
|
172
174
|
let fieldGroupType;
|
|
173
|
-
if (coreStore.resource
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
175
|
+
if (coreStore.resource) {
|
|
176
|
+
if (coreStore.resource.options?.showFieldGroups) {
|
|
177
|
+
fieldGroupType = coreStore.resource.options.showFieldGroups;
|
|
178
|
+
} else if (coreStore.resource.options?.showFieldGroups === null) {
|
|
179
|
+
fieldGroupType = [];
|
|
180
|
+
} else {
|
|
181
|
+
fieldGroupType = coreStore.resource.options?.fieldGroups;
|
|
182
|
+
}
|
|
179
183
|
}
|
|
184
|
+
const activeGroups: typeof fieldGroupType | [] = fieldGroupType ?? [];
|
|
180
185
|
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
return activeGroups.map(group => ({
|
|
186
|
+
return activeGroups.map((group: FieldGroup) => ({
|
|
184
187
|
...group,
|
|
185
|
-
columns: coreStore.resource
|
|
186
|
-
col => group.columns.includes(col.name) && col.showIn
|
|
188
|
+
columns: coreStore.resource?.columns.filter(
|
|
189
|
+
col => group.columns.includes(col.name) && col.showIn?.show
|
|
187
190
|
),
|
|
188
191
|
}));
|
|
189
192
|
});
|
|
190
193
|
|
|
191
194
|
const allColumns = computed(() => {
|
|
192
|
-
return coreStore.resource
|
|
195
|
+
return coreStore.resource?.columns.filter(col => col.showIn?.show);
|
|
193
196
|
});
|
|
194
197
|
|
|
195
198
|
const otherColumns = computed(() => {
|
|
196
199
|
const groupedColumnNames = new Set(
|
|
197
|
-
groups.value.flatMap(group => group.columns.map(col => col.name))
|
|
200
|
+
groups.value.flatMap(group => group.columns.map((col: AdminForthResourceColumnCommon) => col.name))
|
|
198
201
|
);
|
|
199
202
|
|
|
200
|
-
return coreStore.resource
|
|
201
|
-
col => !groupedColumnNames.has(col.name) && col.showIn
|
|
203
|
+
return coreStore.resource?.columns.filter(
|
|
204
|
+
col => !groupedColumnNames.has(col.name) && col.showIn?.show
|
|
202
205
|
);
|
|
203
206
|
});
|
|
204
207
|
|
|
205
|
-
async function deleteRecord(
|
|
208
|
+
async function deleteRecord() {
|
|
206
209
|
const data = await adminforth.confirm({
|
|
207
210
|
message: t('Are you sure you want to delete this item?'),
|
|
208
211
|
yes: t('Delete'),
|
|
@@ -231,7 +234,7 @@ async function deleteRecord(row) {
|
|
|
231
234
|
|
|
232
235
|
}
|
|
233
236
|
|
|
234
|
-
async function startCustomAction(actionId) {
|
|
237
|
+
async function startCustomAction(actionId: string) {
|
|
235
238
|
actionLoadingStates.value[actionId] = true;
|
|
236
239
|
|
|
237
240
|
const data = await callAdminForthApi({
|
|
@@ -263,8 +266,8 @@ async function startCustomAction(actionId) {
|
|
|
263
266
|
|
|
264
267
|
if (data?.ok) {
|
|
265
268
|
await coreStore.fetchRecord({
|
|
266
|
-
resourceId: route.params.resourceId,
|
|
267
|
-
primaryKey: route.params.primaryKey,
|
|
269
|
+
resourceId: route.params.resourceId as string,
|
|
270
|
+
primaryKey: route.params.primaryKey as string,
|
|
268
271
|
source: 'show',
|
|
269
272
|
});
|
|
270
273
|
|
|
@@ -281,4 +284,21 @@ async function startCustomAction(actionId) {
|
|
|
281
284
|
}
|
|
282
285
|
}
|
|
283
286
|
|
|
287
|
+
adminforth.show.refresh = () => {
|
|
288
|
+
(async () => {
|
|
289
|
+
try {
|
|
290
|
+
loading.value = true;
|
|
291
|
+
await coreStore.fetchRecord({
|
|
292
|
+
resourceId: String(route.params.resourceId),
|
|
293
|
+
primaryKey: String(route.params.primaryKey),
|
|
294
|
+
source: 'show',
|
|
295
|
+
});
|
|
296
|
+
} catch (e) {
|
|
297
|
+
showErrorTost((e as Error).message);
|
|
298
|
+
} finally {
|
|
299
|
+
loading.value = false;
|
|
300
|
+
}
|
|
301
|
+
})();
|
|
302
|
+
}
|
|
303
|
+
|
|
284
304
|
</script>
|
|
@@ -1,8 +1,13 @@
|
|
|
1
1
|
|
|
2
2
|
const subscriptions: { [topic: string]: ((data: any) => void)[] } = {};
|
|
3
|
+
|
|
4
|
+
interface ExtendedWebSocket extends WebSocket {
|
|
5
|
+
connected?: boolean;
|
|
6
|
+
}
|
|
7
|
+
|
|
3
8
|
const state: {
|
|
4
9
|
status: 'connecting' | 'connected' | 'disconnected';
|
|
5
|
-
ws:
|
|
10
|
+
ws: ExtendedWebSocket | null;
|
|
6
11
|
} = {
|
|
7
12
|
status: 'connecting',
|
|
8
13
|
ws: null
|