adminforth 2.45.0-next.1 → 2.45.0-next.11

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.
Files changed (41) hide show
  1. package/dist/dataConnectors/clickhouse.d.ts +16 -2
  2. package/dist/dataConnectors/clickhouse.d.ts.map +1 -1
  3. package/dist/dataConnectors/clickhouse.js +65 -0
  4. package/dist/dataConnectors/clickhouse.js.map +1 -1
  5. package/dist/dataConnectors/mongo.d.ts +12 -1
  6. package/dist/dataConnectors/mongo.d.ts.map +1 -1
  7. package/dist/dataConnectors/mongo.js +87 -0
  8. package/dist/dataConnectors/mongo.js.map +1 -1
  9. package/dist/dataConnectors/mysql.d.ts +12 -1
  10. package/dist/dataConnectors/mysql.d.ts.map +1 -1
  11. package/dist/dataConnectors/mysql.js +126 -1
  12. package/dist/dataConnectors/mysql.js.map +1 -1
  13. package/dist/dataConnectors/postgres.js +2 -2
  14. package/dist/dataConnectors/postgres.js.map +1 -1
  15. package/dist/servers/express.d.ts +1 -0
  16. package/dist/servers/express.d.ts.map +1 -1
  17. package/dist/servers/express.js +8 -0
  18. package/dist/servers/express.js.map +1 -1
  19. package/dist/spa/package-lock.json +103 -10
  20. package/dist/spa/package.json +2 -1
  21. package/dist/spa/pnpm-lock.yaml +298 -282
  22. package/dist/spa/src/App.vue +1 -0
  23. package/dist/spa/src/adminforth.ts +5 -3
  24. package/dist/spa/src/afcl/Modal.vue +4 -0
  25. package/dist/spa/src/components/AcceptModal.vue +24 -25
  26. package/dist/spa/src/components/CustomRangePicker.vue +47 -73
  27. package/dist/spa/src/components/ResourceListTable.vue +5 -3
  28. package/dist/spa/src/stores/modal.ts +5 -1
  29. package/dist/spa/src/types/Back.ts +6 -1
  30. package/dist/spa/src/types/FrontendAPI.ts +10 -0
  31. package/dist/spa/src/utils/listUtils.ts +1 -1
  32. package/dist/spa/src/utils/utils.ts +23 -22
  33. package/dist/spa/src/views/ListView.vue +1 -1
  34. package/dist/spa/src/views/ShowView.vue +1 -1
  35. package/dist/types/Back.d.ts +4 -0
  36. package/dist/types/Back.d.ts.map +1 -1
  37. package/dist/types/Back.js.map +1 -1
  38. package/dist/types/FrontendAPI.d.ts +8 -0
  39. package/dist/types/FrontendAPI.d.ts.map +1 -1
  40. package/dist/types/FrontendAPI.js.map +1 -1
  41. package/package.json +1 -1
@@ -135,6 +135,7 @@
135
135
  <component
136
136
  v-for="c in coreStore?.config?.globalInjections?.everyPageBottom || []"
137
137
  :is="getCustomComponent(c)"
138
+ :adminUser="coreStore.adminUser || null"
138
139
  :meta="c.meta"
139
140
  />
140
141
  </div>
@@ -118,11 +118,13 @@ class FrontendAPI implements FrontendAPIInterface {
118
118
 
119
119
  confirm(params: Parameters<FrontendAPIInterface['confirm']>[0]): ReturnType<FrontendAPIInterface['confirm']> {
120
120
  return new Promise((resolve, reject) => {
121
- this.modalStore.setModalContent({
122
- content: params.message,
121
+ this.modalStore.setModalContent({
122
+ title: params.title,
123
+ guardMessage: params.guardMessage,
124
+ content: params.message,
123
125
  contentHTML: params.messageHtml,
124
126
  acceptText: params.yes || 'Yes',
125
- cancelText: params.no || 'Cancel'
127
+ cancelText: params.no || 'Cancel'
126
128
  })
127
129
  this.modalStore.onAcceptFunction = resolve
128
130
  this.modalStore.onCancelFunction = reject
@@ -104,6 +104,10 @@ async function close() {
104
104
  isModalOpen.value = false;
105
105
  }
106
106
 
107
+ defineOptions({
108
+ inheritAttrs: false,
109
+ })
110
+
107
111
  defineExpose({
108
112
  open: open,
109
113
  close: close,
@@ -4,9 +4,9 @@
4
4
  ref="modalRef"
5
5
  :beforeCloseFunction="()=>{modalStore.onAcceptFunction(false);modalStore.isOpened=false}"
6
6
  backgroundCustomClasses="z-[998]"
7
- modalCustomClasses="z-[999]"
7
+ modalCustomClasses="z-[999] flex items-center justify-center"
8
8
  >
9
- <div class="relative p-6 sm:p-8 w-[440px] bg-white rounded-2xl shadow-xl dark:bg-gray-800" >
9
+ <div class="relative p-6 sm:p-8 w-[440px] bg-white rounded-lg shadow-xl dark:bg-gray-800" >
10
10
 
11
11
  <button type="button" @click="modalStore.togleModal()" class="absolute top-4 right-4 text-gray-400 hover:text-gray-900 transition-colors dark:hover:text-white" >
12
12
  <svg class="w-3.5 h-3.5" xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 14 14">
@@ -14,28 +14,30 @@
14
14
  </svg>
15
15
  </button>
16
16
 
17
- <div class="text-center">
18
- <div class="mx-auto flex items-center justify-center h-16 w-16 rounded-full bg-red-50 mb-5 relative dark:bg-red-900/20">
17
+ <div class="text-center flex flex-col">
18
+ <div class="mx-auto flex items-center justify-center h-16 w-16 rounded-full bg-red-50 mb-4 relative dark:bg-red-900/20">
19
19
  <IconClipboardDocumentSolid class="w-10 h-10 text-red-500" />
20
- <div class="absolute bottom-1 right-1 bg-red-500 rounded-full w-[18px] h-[18px] flex items-center justify-center border-2 border-white dark:border-gray-800">
21
- <span class="text-white text-[10px] font-medium">!</span>
20
+ <div class="absolute bottom-1 right-1 bg-red-500 rounded-full w-[18px] h-[18px] flex items-center justify-center border-2 border-white dark:border-gray-800">
21
+ <span class="text-white text-[10px] font-medium">!</span>
22
+ </div>
22
23
  </div>
23
- </div>
24
-
25
- <h3 class="mb-4 text-2xl font-bold text-gray-900 dark:text-white">
26
- There are unsaved changes.
27
- </h3>
28
24
 
29
- <div class="mb-2 text-[15px] text-gray-600 dark:text-gray-300">
30
- <div v-if="modalStore?.modalContent?.contentHTML" class="font-medium" v-html="modalStore.modalContent.contentHTML"></div>
31
- <p v-else-if="modalStore?.modalContent?.content" class="font-medium">{{ modalStore.modalContent.content }}</p>
25
+ <div class="flex flex-col gap-3">
26
+ <h3 v-if="modalStore?.modalContent?.title" class="text-2xl font-bold text-gray-900 dark:text-white">
27
+ {{ modalStore.modalContent.title }}
28
+ </h3>
29
+
30
+ <div class="text-[15px] text-gray-600 dark:text-gray-300">
31
+ <div v-if="modalStore?.modalContent?.contentHTML" class="font-medium" v-html="modalStore.modalContent.contentHTML"></div>
32
+ <p v-else-if="modalStore?.modalContent?.content" class="font-medium">{{ modalStore.modalContent.content }}</p>
33
+ </div>
32
34
  </div>
33
35
 
34
- <hr class="border-t border-gray-100 dark:border-gray-700 mb-6">
36
+ <hr class="border-t-2 border-gray-300 dark:border-gray-700 my-6">
35
37
 
36
38
  <div class="flex justify-center gap-4 w-full">
37
39
  <button @click="()=>{modalStore.onAcceptFunction(false);modalStore.togleModal()}" type="button" class="flex-1 py-2.5 px-4 text-sm font-medium text-gray-700 bg-white rounded-lg border border-gray-200 hover:bg-gray-50 focus:ring-4 focus:ring-gray-100 transition-all dark:bg-gray-800 dark:text-gray-300 dark:border-gray-600 dark:hover:bg-gray-700">
38
- Stay and continue
40
+ {{ modalStore?.modalContent?.cancelText }}
39
41
  </button>
40
42
 
41
43
  <button
@@ -44,18 +46,15 @@
44
46
  class="flex-1 flex items-center justify-center py-2.5 px-4 text-sm font-medium transition-all focus:outline-none
45
47
  text-white bg-red-600 hover:bg-red-700
46
48
  dark:bg-red-500 dark:hover:bg-red-600
47
- border border-red-700 dark:border-red-600
48
- rounded-lg shadow-sm focus:z-10 focus:ring-4
49
+ border-none rounded-lg shadow-sm focus:z-10 focus:ring-4
49
50
  focus:ring-red-100 dark:focus:ring-red-900 gap-1">
50
- Leave without saving
51
+ {{ modalStore?.modalContent?.acceptText }}
51
52
  </button>
52
53
  </div>
53
54
 
54
- <div class="flex items-center justify-center mt-5 text-xs text-gray-400 gap-1.5 font-medium">
55
- <svg class="w-4 h-4" fill="none" stroke="currentColor" viewBox="0 0 24 24">
56
- <path stroke-linecap="round" stroke-linejoin="round" stroke-width="1.5" d="M9 12l2 2 4-4m5.618-4.016A11.955 11.955 0 0112 2.944a11.955 11.955 0 01-8.618 3.04A12.02 12.02 0 003 9c0 5.591 3.824 10.29 9 11.622 5.176-1.332 9-6.03 9-11.622 0-1.042-.133-2.052-.382-3.016z"></path>
57
- </svg>
58
- <span>Your changes will not be saved</span>
55
+ <div v-if="modalStore?.modalContent?.guardMessage" class="flex items-center justify-center mt-6 text-xs text-gray-400 gap-1.5 font-medium">
56
+ <IconShieldCheck class="w-4 h-4" />
57
+ <span> {{ modalStore?.modalContent?.guardMessage }}</span>
59
58
  </div>
60
59
 
61
60
  </div>
@@ -68,7 +67,7 @@
68
67
  import { watch, ref } from 'vue';
69
68
  import { useModalStore } from '@/stores/modal';
70
69
  import { Modal } from '@/afcl';
71
- import { IconClipboardDocumentSolid } from '@iconify-prerendered/vue-heroicons';
70
+ import { IconClipboardDocumentSolid, IconShieldCheck } from '@iconify-prerendered/vue-heroicons';
72
71
 
73
72
 
74
73
  const modalRef = ref();
@@ -18,7 +18,7 @@
18
18
  v-model="end"
19
19
  >
20
20
 
21
- <div v-if="min && max" class="w-full px-2.5">
21
+ <div v-if="min !== undefined && max !== undefined" class="w-full px-2.5">
22
22
  <RangePicker
23
23
  :dot-size="20"
24
24
  height="7.99px"
@@ -31,96 +31,70 @@
31
31
  </div>
32
32
  </template>
33
33
  <script setup lang="ts">
34
- import {computed, onMounted, ref, watch} from "vue";
34
+ import { computed, ref, watch } from "vue";
35
35
  import debounce from 'debounce'
36
36
  import RangePicker from './RangePicker.vue';
37
37
 
38
38
  const props = defineProps<{
39
- valueStart: number | null,
40
- valueEnd: number | null,
41
- min: number,
42
- max: number,
39
+ valueStart: number | null | string,
40
+ valueEnd: number | null | string,
41
+ min: number | string | undefined,
42
+ max: number | string | undefined,
43
43
  }>()
44
44
 
45
45
  const emit = defineEmits(['update:valueStart', 'update:valueEnd']);
46
46
 
47
- const minFormatted = computed(() => Math.floor(<number>props.min));
48
- const maxFormatted = computed(() => Math.ceil(<number>props.max));
47
+ const minFormatted = computed(() => {
48
+ const v = Number(props.min);
49
+ return isNaN(v) ? 0 : Math.floor(v);
50
+ });
49
51
 
52
+ const maxFormatted = computed(() => {
53
+ const v = Number(props.max);
54
+ return isNaN(v) ? 100 : Math.ceil(v);
55
+ });
50
56
 
51
- const start = ref<number | null>(props.valueStart);
52
- const end = ref<number | null>(props.valueEnd);
57
+ const normalize = (val: any) => {
58
+ if (val === "" || val === null || val === undefined) return null;
59
+ const numericValue = Number(val);
60
+ return isNaN(numericValue) ? null : numericValue;
61
+ };
53
62
 
54
- const sliderValue = ref<[number, number]>([minFormatted.value, maxFormatted.value]);
63
+ const start = ref<number | null>(normalize(props.valueStart));
64
+ const end = ref<number | null>(normalize(props.valueEnd));
55
65
 
56
- watch([start, end], () => {
57
- if ( !start.value && end.value ) {
58
- setSliderValues(minFormatted.value, end.value);
59
- } else if ( start.value && !end.value ) {
60
- setSliderValues(start.value, maxFormatted.value);
61
- } else if ( !start.value && !end.value ) {
62
- setSliderValues(minFormatted.value, maxFormatted.value);
63
- } else {
64
- setSliderValues(start.value, end.value);
65
- }
66
- })
66
+ const sliderValue = ref<[number, number]>([
67
+ start.value ?? minFormatted.value,
68
+ end.value ?? maxFormatted.value
69
+ ]);
67
70
 
68
- const updateFromSlider =
69
- debounce((value: [number, number]) => {
70
- start.value = value[0] === minFormatted.value ? null : value[0];
71
- end.value = value[1] === maxFormatted.value ? null : value[1];
72
- }, 500);
71
+ function setSliderValues(s: number | null, e: number | null) {
72
+ sliderValue.value = [s ?? minFormatted.value, e ?? maxFormatted.value];
73
+ }
73
74
 
74
- onMounted(() => {
75
- updateStartFromProps();
76
- updateEndFromProps();
75
+ watch([start, end], () => {
76
+ setSliderValues(start.value, end.value);
77
+ }, { immediate: true });
77
78
 
78
- watch(() => props.valueStart, (value) => {
79
- updateStartFromProps();
80
- });
79
+ const updateFromSlider = debounce((value: [number, number]) => {
80
+ start.value = value[0] === minFormatted.value ? null : value[0];
81
+ end.value = value[1] === maxFormatted.value ? null : value[1];
82
+ }, 500);
81
83
 
82
- watch(() => props.valueEnd, (value) => {
83
- updateEndFromProps();
84
- });
85
- })
84
+ watch(() => props.valueStart, (newVal) => {
85
+ const v = normalize(newVal);
86
+ if (v !== start.value) start.value = v;
87
+ });
86
88
 
87
- function updateStartFromProps() {
88
- if (props.valueStart == start.value) {
89
- return;
90
- }
91
- start.value = props.valueStart;
92
- setSliderValues(start.value, end.value)
93
- }
89
+ watch(() => props.valueEnd, (newVal) => {
90
+ const v = normalize(newVal);
91
+ if (v !== end.value) end.value = v;
92
+ });
94
93
 
95
- function updateEndFromProps() {
96
- if (props.valueEnd == end.value) {
97
- return;
98
- }
99
- end.value = props.valueEnd;
100
- setSliderValues(start.value, end.value)
101
- }
94
+ watch(start, (newVal) => emit('update:valueStart', newVal));
95
+ watch(end, (newVal) => emit('update:valueEnd', newVal));
102
96
 
103
- watch(start, () => {
104
- emit('update:valueStart', start.value)
97
+ watch([minFormatted, maxFormatted], () => {
98
+ setSliderValues(start.value, end.value);
105
99
  })
106
-
107
- watch(end, () => {
108
- emit('update:valueEnd', end.value);
109
- })
110
-
111
- watch([minFormatted,maxFormatted], () => {
112
- if ( !start.value && end.value ) {
113
- setSliderValues(minFormatted.value, end.value);
114
- } else if ( start.value && !end.value ) {
115
- setSliderValues(start.value, maxFormatted.value);
116
- } else if ( !start.value && !end.value ) {
117
- setSliderValues(minFormatted.value, maxFormatted.value);
118
- } else {
119
- setSliderValues(start.value, end.value);
120
- }
121
- })
122
-
123
- function setSliderValues(start: any, end: any) {
124
- sliderValue.value = [start || minFormatted.value, end || maxFormatted.value];
125
- }
126
100
  </script>
@@ -372,7 +372,7 @@ import CallActionWrapper from '@/components/CallActionWrapper.vue'
372
372
  const coreStore = useCoreStore();
373
373
  const { t } = useI18n();
374
374
  const { alert, confirm } = useAdminforth();
375
- const props = defineProps<{
375
+ const props = withDefaults(defineProps<{
376
376
  page: number,
377
377
  resource: AdminForthResourceFrontend | null,
378
378
  rows: any[] | null,
@@ -389,7 +389,9 @@ const props = defineProps<{
389
389
  customActionIconsThreeDotsMenuItems?: AdminForthComponentDeclaration[]
390
390
  tableRowReplaceInjection?: AdminForthComponentDeclaration,
391
391
  isVirtualScrollEnabled: boolean
392
- }>();
392
+ }>(), {
393
+ sort: () => []
394
+ });
393
395
 
394
396
  //select between all rows or rows, that should be rendered in virtual scroll
395
397
  const rowsToRender = computed(() => {
@@ -582,7 +584,7 @@ async function onClick(e: any, row: any) {
582
584
 
583
585
  async function deleteRecord(row: any) {
584
586
  const data = await confirm({
585
- message: t('Are you sure you want to delete this item?'),
587
+ title: t('Are you sure you want to delete this item?'),
586
588
  yes: t('Delete'),
587
589
  no: t('Cancel'),
588
590
  });
@@ -3,6 +3,7 @@ import { defineStore } from 'pinia'
3
3
 
4
4
  type ModalContentType = {
5
5
  title?: string;
6
+ guardMessage?: string;
6
7
  content?: string;
7
8
  contentHTML?: string;
8
9
  acceptText?: string;
@@ -13,6 +14,7 @@ import { defineStore } from 'pinia'
13
14
  export const useModalStore = defineStore('modal', () => {
14
15
  const modalContent = ref({
15
16
  title: 'title',
17
+ guardMessage: 'guardMessage',
16
18
  content: '',
17
19
  contentHTML: '',
18
20
  acceptText: 'acceptText',
@@ -32,7 +34,8 @@ export const useModalStore = defineStore('modal', () => {
32
34
  }
33
35
  function setModalContent(content: ModalContentType) {
34
36
  modalContent.value = {
35
- title: content.title || 'title',
37
+ title: content.title || '',
38
+ guardMessage: content.guardMessage || '',
36
39
  content: content.content || '',
37
40
  contentHTML: content.contentHTML || '',
38
41
  acceptText: content.acceptText || 'acceptText',
@@ -43,6 +46,7 @@ export const useModalStore = defineStore('modal', () => {
43
46
  isOpened.value = false;
44
47
  modalContent.value = {
45
48
  title: 'title',
49
+ guardMessage: 'guardMessage',
46
50
  content: 'content',
47
51
  contentHTML: '',
48
52
  acceptText: 'acceptText',
@@ -153,6 +153,11 @@ export interface IExpressHttpServer extends IHttpServer {
153
153
  listen(port: number, callback: Function): void;
154
154
  listen(port: number, host: string, callback: Function): void;
155
155
 
156
+ /**
157
+ * Returns an internal HTTP origin for same-process/server API calls.
158
+ */
159
+ getInternalApiOrigin(): string | undefined;
160
+
156
161
  /**
157
162
  * Method (middleware) to wrap express endpoints with authorization check.
158
163
  * Adds adminUser to request object if user is authorized. Drops request with 401 status if user is not authorized.
@@ -2328,4 +2333,4 @@ export interface PluginsCommonOptions {
2328
2333
  * It is required if you want to log custom actions, otherwise it is optional.
2329
2334
  */
2330
2335
  id?: string;
2331
- }
2336
+ }
@@ -160,6 +160,16 @@ export interface FrontendAPIInterface {
160
160
  }
161
161
 
162
162
  export type ConfirmParams = {
163
+ /**
164
+ * The title to display in the dialog
165
+ */
166
+ title?: string;
167
+
168
+ /**
169
+ * The message to display in the dialog as a warning that action is irreversible
170
+ */
171
+ guardMessage?: string;
172
+
163
173
  /**
164
174
  * The message to display in the dialog
165
175
  */
@@ -60,7 +60,7 @@ export async function startBulkAction(actionId: string, resource: AdminForthReso
60
60
 
61
61
  if (action?.confirm) {
62
62
  const confirmed = await confirm({
63
- message: action.confirm,
63
+ title: action.confirm,
64
64
  });
65
65
  if (!confirmed) {
66
66
  return;
@@ -6,7 +6,7 @@ import { useCoreStore } from '../stores/core';
6
6
  import { useUserStore } from '../stores/user';
7
7
  import { Dropdown } from 'flowbite';
8
8
  import adminforth, { useAdminforth } from '../adminforth';
9
- import sanitizeHtml from 'sanitize-html'
9
+ import DOMPurify from 'dompurify'
10
10
  import debounce from 'debounce';
11
11
  import type { AdminForthActionFront, AdminForthResourceColumnInputCommon, AdminForthResourceFrontend, Predicate } from '@/types/Common';
12
12
  import { i18nInstance } from '../i18n'
@@ -337,26 +337,25 @@ export function humanifySize(size: number) {
337
337
  }
338
338
 
339
339
  export function protectAgainstXSS(value: string) {
340
- return sanitizeHtml(value, {
341
- allowedTags: [
342
- "address", "article", "aside", "footer", "header", "h1", "h2", "h3", "h4",
343
- "h5", "h6", "hgroup", "main", "nav", "section", "blockquote", "dd", "div",
344
- "dl", "dt", "figcaption", "figure", "hr", "li", "main", "ol", "p", "pre",
345
- "ul", "a", "abbr", "b", "bdi", "bdo", "br", "cite", "code", "data", "dfn",
346
- "em", "i", "kbd", "mark", "q", "rb", "rp", "rt", "rtc", "ruby", "s", "samp",
347
- "small", "span", "strong", "sub", "sup", "time", "u", "var", "wbr", "caption",
348
- "col", "colgroup", "table", "tbody", "td", "tfoot", "th", "thead", "tr", 'img', 'video', 'source'
340
+ return DOMPurify.sanitize(value, {
341
+ ALLOWED_TAGS: [
342
+ "address","article","aside","footer","header","h1","h2","h3","h4",
343
+ "h5","h6","hgroup","main","nav","section","blockquote","dd","div",
344
+ "dl","dt","figcaption","figure","hr","li","ol","p","pre",
345
+ "ul","a","abbr","b","bdi","bdo","br","cite","code","data","dfn",
346
+ "em","i","kbd","mark","q","rb","rp","rt","rtc","ruby","s","samp",
347
+ "small","span","strong","sub","sup","time","u","var","wbr","caption",
348
+ "col","colgroup","table","tbody","td","tfoot","th","thead","tr",
349
+ "img","video","source"
349
350
  ],
350
- allowedAttributes: {
351
- 'li': [ 'data-list' ],
352
- 'img': [ 'src', 'srcset', 'alt', 'title', 'width', 'height', 'loading' ],
353
- 'video': [ 'src', 'controls', 'autoplay', 'loop', 'muted', 'poster', 'width', 'height', 'autoplay', 'playsinline' ],
354
- 'source': [ 'src', 'type' ],
355
- // Allow markup on spans (classes & styles), and
356
- // generic data/aria/style attributes on any element. (e.g. for KaTeX-related previews)
357
- 'span': [ 'class', 'style' ],
358
- '*': [ 'data-*', 'aria-*', 'style' ]
359
- },
351
+ ALLOWED_ATTR: [
352
+ "data-list",
353
+ "src","srcset","alt","title","width","height","loading",
354
+ "controls","autoplay","loop","muted","poster","playsinline","type",
355
+ "class","style"
356
+ ],
357
+ ALLOW_DATA_ATTR: true,
358
+ ALLOW_ARIA_ATTR: true
360
359
  });
361
360
  }
362
361
 
@@ -781,9 +780,11 @@ export async function onBeforeRouteLeaveCreateEditViewGuard(initialValues: any,
781
780
  generateMessageHtmlForRecordChange(changedFields, t);
782
781
 
783
782
  const answer = await confirm({
783
+ title: t('There are unsaved changes'),
784
+ guardMessage: t('Your changes will not be saved'),
784
785
  messageHtml,
785
- yes: t('Yes'),
786
- no: t('No'),
786
+ yes: t('Leave without saving'),
787
+ no: t('Stay and continue'),
787
788
  });
788
789
 
789
790
  return answer;
@@ -224,7 +224,7 @@ import { getCustomComponent, initThreeDotsDropdown, getList, startBulkAction } f
224
224
  import ThreeDotsMenu from '@/components/ThreeDotsMenu.vue';
225
225
  import { Tooltip, Spinner } from '@/afcl'
226
226
  import type { AdminForthComponentDeclaration, AdminForthComponentDeclarationFull, AdminForthFilterOperators, AdminForthResourceColumnCommon } from '@/types/Common';
227
-
227
+ import { useI18n } from 'vue-i18n';
228
228
 
229
229
  import {
230
230
  IconBanOutline,
@@ -310,7 +310,7 @@ const otherColumns = computed(() => {
310
310
 
311
311
  async function deleteRecord() {
312
312
  const data = await confirm({
313
- message: t('Are you sure you want to delete this item?'),
313
+ title: t('Are you sure you want to delete this item?'),
314
314
  yes: t('Delete'),
315
315
  no: t('Cancel'),
316
316
  });
@@ -124,6 +124,10 @@ export interface IExpressHttpServer extends IHttpServer {
124
124
  */
125
125
  listen(port: number, callback: Function): void;
126
126
  listen(port: number, host: string, callback: Function): void;
127
+ /**
128
+ * Returns an internal HTTP origin for same-process/server API calls.
129
+ */
130
+ getInternalApiOrigin(): string | undefined;
127
131
  /**
128
132
  * Method (middleware) to wrap express endpoints with authorization check.
129
133
  * Adds adminUser to request object if user is authorized. Drops request with 401 status if user is not authorized.