adminforth 2.4.0-next.13 → 2.4.0-next.131

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 (149) hide show
  1. package/commands/callTsProxy.js +14 -4
  2. package/commands/cli.js +12 -4
  3. package/commands/createApp/templates/custom/tsconfig.json.hbs +2 -3
  4. package/commands/createApp/templates/index.ts.hbs +10 -2
  5. package/commands/createApp/templates/package.json.hbs +1 -1
  6. package/commands/createApp/utils.js +27 -2
  7. package/commands/createCustomComponent/configLoader.js +3 -0
  8. package/dist/dataConnectors/baseConnector.d.ts.map +1 -1
  9. package/dist/dataConnectors/baseConnector.js +16 -3
  10. package/dist/dataConnectors/baseConnector.js.map +1 -1
  11. package/dist/dataConnectors/clickhouse.d.ts.map +1 -1
  12. package/dist/dataConnectors/clickhouse.js +4 -0
  13. package/dist/dataConnectors/clickhouse.js.map +1 -1
  14. package/dist/dataConnectors/mongo.d.ts.map +1 -1
  15. package/dist/dataConnectors/mongo.js +14 -14
  16. package/dist/dataConnectors/mongo.js.map +1 -1
  17. package/dist/index.d.ts +2 -1
  18. package/dist/index.d.ts.map +1 -1
  19. package/dist/index.js +20 -9
  20. package/dist/index.js.map +1 -1
  21. package/dist/modules/codeInjector.d.ts.map +1 -1
  22. package/dist/modules/codeInjector.js +22 -5
  23. package/dist/modules/codeInjector.js.map +1 -1
  24. package/dist/modules/configValidator.d.ts.map +1 -1
  25. package/dist/modules/configValidator.js +53 -2
  26. package/dist/modules/configValidator.js.map +1 -1
  27. package/dist/modules/restApi.d.ts.map +1 -1
  28. package/dist/modules/restApi.js +147 -25
  29. package/dist/modules/restApi.js.map +1 -1
  30. package/dist/modules/styles.d.ts +457 -13
  31. package/dist/modules/styles.d.ts.map +1 -1
  32. package/dist/modules/styles.js +513 -31
  33. package/dist/modules/styles.js.map +1 -1
  34. package/dist/modules/utils.d.ts +1 -0
  35. package/dist/modules/utils.d.ts.map +1 -1
  36. package/dist/modules/utils.js +9 -0
  37. package/dist/modules/utils.js.map +1 -1
  38. package/dist/spa/index.html +1 -1
  39. package/dist/spa/src/App.vue +33 -15
  40. package/dist/spa/src/adminforth.ts +15 -6
  41. package/dist/spa/src/afcl/BarChart.vue +2 -2
  42. package/dist/spa/src/afcl/Button.vue +6 -6
  43. package/dist/spa/src/afcl/Checkbox.vue +21 -13
  44. package/dist/spa/src/afcl/CountryFlag.vue +4 -1
  45. package/dist/spa/src/{components/CustomDatePicker.vue → afcl/DatePicker.vue} +95 -9
  46. package/dist/spa/src/afcl/Dialog.vue +44 -27
  47. package/dist/spa/src/afcl/Dropzone.vue +12 -12
  48. package/dist/spa/src/afcl/Input.vue +9 -7
  49. package/dist/spa/src/afcl/JsonViewer.vue +25 -0
  50. package/dist/spa/src/afcl/Link.vue +1 -1
  51. package/dist/spa/src/afcl/LinkButton.vue +2 -2
  52. package/dist/spa/src/afcl/PieChart.vue +5 -5
  53. package/dist/spa/src/afcl/ProgressBar.vue +7 -7
  54. package/dist/spa/src/afcl/Select.vue +68 -34
  55. package/dist/spa/src/afcl/Skeleton.vue +6 -6
  56. package/dist/spa/src/afcl/Table.vue +171 -39
  57. package/dist/spa/src/afcl/Textarea.vue +31 -0
  58. package/dist/spa/src/afcl/Toggle.vue +32 -0
  59. package/dist/spa/src/afcl/Tooltip.vue +2 -3
  60. package/dist/spa/src/afcl/VerticalTabs.vue +3 -3
  61. package/dist/spa/src/afcl/index.ts +4 -3
  62. package/dist/spa/src/components/AcceptModal.vue +7 -7
  63. package/dist/spa/src/components/Breadcrumbs.vue +5 -5
  64. package/dist/spa/src/components/ColumnValueInput.vue +37 -18
  65. package/dist/spa/src/components/ColumnValueInputWrapper.vue +4 -3
  66. package/dist/spa/src/components/CustomDateRangePicker.vue +9 -8
  67. package/dist/spa/src/components/CustomRangePicker.vue +37 -8
  68. package/dist/spa/src/components/Filters.vue +83 -37
  69. package/dist/spa/src/components/GroupsTable.vue +9 -8
  70. package/dist/spa/src/components/MenuLink.vue +3 -3
  71. package/dist/spa/src/components/ResourceForm.vue +94 -51
  72. package/dist/spa/src/components/ResourceListTable.vue +78 -80
  73. package/dist/spa/src/components/ResourceListTableVirtual.vue +70 -72
  74. package/dist/spa/src/components/ShowTable.vue +17 -12
  75. package/dist/spa/src/components/SingleSkeletLoader.vue +6 -6
  76. package/dist/spa/src/components/SkeleteLoader.vue +3 -3
  77. package/dist/spa/src/components/ThreeDotsMenu.vue +73 -14
  78. package/dist/spa/src/components/Toast.vue +2 -7
  79. package/dist/spa/src/components/ValueRenderer.vue +43 -16
  80. package/dist/spa/src/controls/BoolToggle.vue +34 -0
  81. package/dist/spa/src/i18n.ts +1 -1
  82. package/dist/spa/src/shims-vue.d.ts +5 -0
  83. package/dist/spa/src/spa_types/core.ts +8 -1
  84. package/dist/spa/src/stores/core.ts +1 -1
  85. package/dist/spa/src/stores/modal.ts +6 -1
  86. package/dist/spa/src/types/Back.ts +93 -21
  87. package/dist/spa/src/types/Common.ts +44 -31
  88. package/dist/spa/src/types/FrontendAPI.ts +8 -0
  89. package/dist/spa/src/types/adapters/CompletionAdapter.ts +25 -0
  90. package/dist/spa/src/types/adapters/EmailAdapter.ts +27 -0
  91. package/dist/spa/src/types/adapters/ImageGenerationAdapter.ts +50 -0
  92. package/dist/spa/src/types/adapters/ImageVisionAdapter.ts +30 -0
  93. package/dist/spa/src/types/adapters/OAuth2Adapter.ts +34 -0
  94. package/dist/spa/src/types/adapters/StorageAdapter.ts +73 -0
  95. package/dist/spa/src/types/adapters/index.ts +6 -0
  96. package/dist/spa/src/utils.ts +217 -7
  97. package/dist/spa/src/views/CreateView.vue +18 -19
  98. package/dist/spa/src/views/EditView.vue +25 -19
  99. package/dist/spa/src/views/ListView.vue +117 -79
  100. package/dist/spa/src/views/LoginView.vue +34 -35
  101. package/dist/spa/src/views/ResourceParent.vue +1 -1
  102. package/dist/spa/src/views/ShowView.vue +59 -39
  103. package/dist/spa/src/websocket.ts +6 -1
  104. package/dist/spa/tsconfig.app.json +1 -1
  105. package/dist/spa/vite.config.ts +45 -2
  106. package/dist/types/Back.d.ts +69 -14
  107. package/dist/types/Back.d.ts.map +1 -1
  108. package/dist/types/Back.js +3 -0
  109. package/dist/types/Back.js.map +1 -1
  110. package/dist/types/Common.d.ts +37 -28
  111. package/dist/types/Common.d.ts.map +1 -1
  112. package/dist/types/Common.js.map +1 -1
  113. package/dist/types/FrontendAPI.d.ts +7 -0
  114. package/dist/types/FrontendAPI.d.ts.map +1 -1
  115. package/dist/types/FrontendAPI.js.map +1 -1
  116. package/dist/types/adapters/CompletionAdapter.d.ts +20 -0
  117. package/dist/types/adapters/CompletionAdapter.d.ts.map +1 -0
  118. package/dist/types/adapters/CompletionAdapter.js +2 -0
  119. package/dist/types/adapters/CompletionAdapter.js.map +1 -0
  120. package/dist/types/adapters/EmailAdapter.d.ts +20 -0
  121. package/dist/types/adapters/EmailAdapter.d.ts.map +1 -0
  122. package/dist/types/adapters/EmailAdapter.js +2 -0
  123. package/dist/types/adapters/EmailAdapter.js.map +1 -0
  124. package/dist/types/adapters/ImageGenerationAdapter.d.ts +37 -0
  125. package/dist/types/adapters/ImageGenerationAdapter.d.ts.map +1 -0
  126. package/dist/types/adapters/ImageGenerationAdapter.js +2 -0
  127. package/dist/types/adapters/ImageGenerationAdapter.js.map +1 -0
  128. package/dist/types/adapters/ImageVisionAdapter.d.ts +25 -0
  129. package/dist/types/adapters/ImageVisionAdapter.d.ts.map +1 -0
  130. package/dist/types/adapters/ImageVisionAdapter.js +2 -0
  131. package/dist/types/adapters/ImageVisionAdapter.js.map +1 -0
  132. package/dist/types/adapters/OAuth2Adapter.d.ts +32 -0
  133. package/dist/types/adapters/OAuth2Adapter.d.ts.map +1 -0
  134. package/dist/types/adapters/OAuth2Adapter.js +2 -0
  135. package/dist/types/adapters/OAuth2Adapter.js.map +1 -0
  136. package/dist/types/adapters/StorageAdapter.d.ts +63 -0
  137. package/dist/types/adapters/StorageAdapter.d.ts.map +1 -0
  138. package/dist/types/adapters/StorageAdapter.js +2 -0
  139. package/dist/types/adapters/StorageAdapter.js.map +1 -0
  140. package/dist/types/adapters/index.d.ts +7 -0
  141. package/dist/types/adapters/index.d.ts.map +1 -0
  142. package/dist/types/adapters/index.js +2 -0
  143. package/dist/types/adapters/index.js.map +1 -0
  144. package/package.json +2 -2
  145. package/dist/spa/src/types/Adapters.ts +0 -213
  146. package/dist/types/Adapters.d.ts +0 -168
  147. package/dist/types/Adapters.d.ts.map +0 -1
  148. package/dist/types/Adapters.js +0 -2
  149. package/dist/types/Adapters.js.map +0 -1
@@ -1,13 +1,8 @@
1
1
  <template>
2
2
 
3
3
 
4
- <div class="flex items-center w-full p-4 text-gray-500 rounded-lg shadow-lg dark:text-gray-400 dark:bg-gray-800 bg-white"
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-white text-gray-400 hover:text-gray-900 rounded-lg focus:ring-2 focus:ring-gray-300 p-1.5 hover:bg-gray-100 inline-flex items-center justify-center h-8 w-8 dark:text-gray-500 dark:hover:text-white dark:bg-gray-800 dark:hover:bg-gray-700" >
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>
@@ -12,13 +12,40 @@
12
12
  >
13
13
  <RouterLink
14
14
  class="font-medium text-lightSidebarText dark:text-darkSidebarText hover:brightness-110 whitespace-nowrap"
15
- :to="{ name: 'resource-show', params: { primaryKey: foreignResource.pk, resourceId: column.foreignResource.resourceId || column.foreignResource.polymorphicResources.find((pr) => pr.whenValue === record[column.foreignResource.polymorphicOn]).resourceId } }"
15
+ :to="{
16
+ name: 'resource-show',
17
+ params: {
18
+ primaryKey: foreignResource.pk,
19
+ resourceId: column.foreignResource
20
+ ? (
21
+ column.foreignResource.resourceId
22
+ || column.foreignResource.polymorphicResources?.find(
23
+ (pr: any) => pr.whenValue === record[column.foreignResource?.polymorphicOn!]
24
+ )?.resourceId
25
+ )
26
+ : undefined
27
+ }
28
+ }"
16
29
  >
17
30
  {{ foreignResource.label }}
18
31
  </RouterLink>
19
32
  </span>
20
33
  <RouterLink v-else-if="record[column.name]" class="font-medium text-lightPrimary dark:text-darkPrimary hover:brightness-110 whitespace-nowrap"
21
- :to="{ name: 'resource-show', params: { primaryKey: record[column.name].pk, resourceId: column.foreignResource.resourceId || column.foreignResource.polymorphicResources.find((pr) => pr.whenValue === record[column.foreignResource.polymorphicOn]).resourceId } }">
34
+ :to="{
35
+ name: 'resource-show',
36
+ params: {
37
+ primaryKey: record[column.name].pk,
38
+ resourceId: column.foreignResource
39
+ ? (
40
+ column.foreignResource.resourceId
41
+ || column.foreignResource.polymorphicResources?.find(
42
+ (pr: any) => pr.whenValue === record[column.foreignResource?.polymorphicOn!]
43
+ )?.resourceId
44
+ )
45
+ : undefined
46
+ }
47
+ }"
48
+ >
22
49
  {{ record[column.name].label }}
23
50
  </RouterLink>
24
51
  <div v-else>
@@ -27,8 +54,8 @@
27
54
  </span>
28
55
 
29
56
  <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-800gg 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>
57
+ <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>
58
+ <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
59
  <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
60
  </span>
34
61
  <span
@@ -38,14 +65,14 @@
38
65
  <template v-for="(arrayItem, arrayItemIndex) in record[column.name]">
39
66
  <span
40
67
  v-if="column.isArray.itemType === 'boolean' && arrayItem"
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">
68
+ :key="`${column.name}-${arrayItemIndex}-true`"
69
+ 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
70
  {{ $t('Yes') }}
44
71
  </span>
45
72
  <span
46
73
  v-else-if="column.isArray.itemType === 'boolean'"
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">
74
+ :key="`${column.name}-${arrayItemIndex}-false`"
75
+ 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
76
  {{ $t('No') }}
50
77
  </span>
51
78
  <span
@@ -53,30 +80,30 @@
53
80
  :key="`${column.name}-${arrayItemIndex}`"
54
81
  class="rounded-md m-0.5 bg-lightAnnouncementBG dark:bg-darkAnnouncementBG text-lightAnnouncementText dark:text-darkAnnouncementText py-0.5 px-2.5 text-sm"
55
82
  >
56
- {{ checkEmptyValues(getArrayItemDisplayValue(arrayItem, column), route.meta.type) }}
83
+ {{ checkEmptyValues(getArrayItemDisplayValue(arrayItem, column), route.meta.type as "show" | "list") }}
57
84
  </span>
58
85
  </template>
59
86
  </span>
60
87
  <span v-else-if="column.enum">
61
- {{ checkEmptyValues(column.enum.find(e => e.value === record[column.name])?.label || record[column.name], route.meta.type) }}
88
+ {{ checkEmptyValues(column.enum.find(e => e.value === record[column.name])?.label || record[column.name], route.meta.type as "show" | "list") }}
62
89
  </span>
63
90
  <span v-else-if="column.type === 'datetime'" class="whitespace-nowrap">
64
- {{ checkEmptyValues(formatDateTime(record[column.name]), route.meta.type) }}
91
+ {{ checkEmptyValues(formatDateTime(record[column.name]), route.meta.type as "show" | "list") }}
65
92
  </span>
66
93
  <span v-else-if="column.type === 'date'" class="whitespace-nowrap">
67
- {{ checkEmptyValues(formatDate(record[column.name]), route.meta.type) }}
94
+ {{ checkEmptyValues(formatDate(record[column.name]), route.meta.type as "show" | "list") }}
68
95
  </span>
69
96
  <span v-else-if="column.type === 'time'" class="whitespace-nowrap">
70
- {{ checkEmptyValues(formatTime(record[column.name]), route.meta.type) }}
97
+ {{ checkEmptyValues(formatTime(record[column.name]), route.meta.type as "show" | "list") }}
71
98
  </span>
72
99
  <span v-else-if="column.type === 'decimal'">
73
- {{ checkEmptyValues(record[column.name] && parseFloat(record[column.name]), route.meta.type) }}
100
+ {{ checkEmptyValues(record[column.name] && parseFloat(record[column.name]), route.meta.type as "show" | "list") }}
74
101
  </span>
75
102
  <span v-else-if="column.type === 'json'">
76
103
  <JsonViewer class="min-w-[6rem]" :value="record[column.name]" :expandDepth="column.extra?.jsonCollapsedLevel" copyable sort :theme="coreStore.theme"/>
77
104
  </span>
78
105
  <span v-else>
79
- {{ checkEmptyValues(record[column.name],route.meta.type) }}
106
+ {{ checkEmptyValues(record[column.name], route.meta.type as "show" | "list") }}
80
107
  </span>
81
108
  </div>
82
109
  </template>
@@ -122,7 +149,7 @@ function formatTime(time: string) {
122
149
  return dayjs(`0000-00-00 ${time}`).format(coreStore.config?.timeFormat || 'HH:mm:ss');
123
150
  }
124
151
 
125
- function getArrayItemDisplayValue(value, column) {
152
+ function getArrayItemDisplayValue(value: any, column: AdminForthResourceColumnCommon) {
126
153
  if (column.isArray?.itemType === 'datetime') {
127
154
  return formatDateTime(value);
128
155
  } else if (column.isArray?.itemType === 'date') {
@@ -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>
@@ -3,7 +3,7 @@ import { createApp } from 'vue';
3
3
 
4
4
 
5
5
  // taken from here https://vue-i18n.intlify.dev/guide/essentials/pluralization.html#custom-pluralization
6
- function slavicPluralRule(choice, choicesLength, orgRule) {
6
+ function slavicPluralRule(choice: number, choicesLength: number, orgRule: any) {
7
7
  if (choice === 0) {
8
8
  return 0
9
9
  }
@@ -0,0 +1,5 @@
1
+ declare module '*.vue' {
2
+ import type { DefineComponent } from 'vue';
3
+ const component: DefineComponent<{}, {}, any>;
4
+ export default component;
5
+ }
@@ -1,4 +1,4 @@
1
- import type { AdminForthResource, AdminForthResourceColumn } from '../types/AdminForthConfig';
1
+ import type { AdminForthResource, AdminForthResourceColumn } from '../types/Back.js';
2
2
 
3
3
  export type resourceById = {
4
4
  [key: string]: AdminForthResource;
@@ -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
 
@@ -118,7 +118,7 @@ export const useCoreStore = defineStore('core', () => {
118
118
  item.badge = badge;
119
119
  }
120
120
  });
121
-
121
+ websocket.unsubscribeAll();
122
122
  subscribeToMenuBadges();
123
123
 
124
124
  }
@@ -29,7 +29,12 @@ export const useModalStore = defineStore('modal', () => {
29
29
  onCancelFunction.value = func;
30
30
  }
31
31
  function setModalContent(content: ModalContentType) {
32
- modalContent.value = content;
32
+ modalContent.value = {
33
+ title: content.title || 'title',
34
+ content: content.content || 'content',
35
+ acceptText: content.acceptText || 'acceptText',
36
+ cancelText: content.cancelText || 'cancelText',
37
+ };
33
38
  }
34
39
  function resetmodalState() {
35
40
  isOpened.value = false;
@@ -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,
@@ -8,12 +8,12 @@ import { ActionCheckSource, AdminForthFilterOperators, AdminForthSortDirections,
8
8
  type AdminForthBulkActionCommon,
9
9
  type AdminForthForeignResourceCommon,
10
10
  type AdminForthResourceColumnCommon,
11
- AdminForthResourceInputCommon,
12
- AdminForthComponentDeclarationFull,
13
- AdminForthConfigMenuItem,
14
- AnnouncementBadgeResponse,
11
+ type AdminForthResourceInputCommon,
12
+ type AdminForthComponentDeclarationFull,
13
+ type AdminForthConfigMenuItem,
14
+ type AnnouncementBadgeResponse,
15
15
  AdminForthResourcePages,
16
- AdminForthResourceColumnInputCommon,
16
+ type AdminForthResourceColumnInputCommon,
17
17
  } from './Common.js';
18
18
 
19
19
  export interface ICodeInjector {
@@ -92,19 +92,36 @@ export interface IExpressHttpServer extends IHttpServer {
92
92
  * Adds adminUser to request object if user is authorized. Drops request with 401 status if user is not authorized.
93
93
  * @param callable : Function which will be called if user is authorized.
94
94
  *
95
- * Example:
96
95
  *
96
+ * @example
97
97
  * ```ts
98
- * expressApp.get('/myApi', authorize((req, res) => \{
98
+ * expressApp.get('/myApi', authorize((req, res) => {
99
99
  * console.log('User is authorized', req.adminUser);
100
- * res.json(\{ message: 'Hello World' \});
101
- * \}));
102
- * ``
100
+ * res.json({ message: 'Hello World' });
101
+ * }));
102
+ * ```
103
103
  *
104
- */
104
+ */
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,12 +636,23 @@ 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
625
647
  */
626
648
  showBrandNameInSidebar?: boolean,
627
649
 
650
+ /**
651
+ * Whether to show brand logo in sidebar
652
+ * default is true
653
+ */
654
+ showBrandLogoInSidebar?: boolean,
655
+
628
656
  /**
629
657
  * Path to your app logo
630
658
  *
@@ -757,8 +785,19 @@ interface AdminForthInputConfigCustomization {
757
785
  userMenu?: AdminForthComponentDeclaration | Array<AdminForthComponentDeclaration>,
758
786
  header?: AdminForthComponentDeclaration | Array<AdminForthComponentDeclaration>,
759
787
  sidebar?: AdminForthComponentDeclaration | Array<AdminForthComponentDeclaration>,
788
+ sidebarTop?: AdminForthComponentDeclaration | Array<AdminForthComponentDeclaration>,
760
789
  everyPageBottom?: AdminForthComponentDeclaration | Array<AdminForthComponentDeclaration>,
761
790
  }
791
+
792
+ /**
793
+ * Allows adding custom elements (e.g., <link>, <script>, <meta>) to the <head> of the HTML document.
794
+ * Each item must include a tag name and a set of attributes.
795
+ */
796
+ customHeadItems?: {
797
+ tagName: string;
798
+ attributes: Record<string, string | boolean>;
799
+ }[];
800
+
762
801
  }
763
802
 
764
803
  export interface AdminForthActionInput {
@@ -935,6 +974,13 @@ export interface AdminForthInputConfig {
935
974
  */
936
975
  loginBackgroundPosition?: 'over' | '1/2' | '1/3' | '2/3' | '3/4' | '2/5' | '3/5',
937
976
 
977
+ /**
978
+ * If true, background blend mode will be removed from login background image when position is 'over'
979
+ *
980
+ * Default: false
981
+ */
982
+ removeBackgroundBlendMode?: boolean,
983
+
938
984
  /**
939
985
  * Function or functions which will be called before user try to login.
940
986
  * Each function will resive User object as an argument
@@ -956,7 +1002,7 @@ export interface AdminForthInputConfig {
956
1002
  /**
957
1003
  * Any prompt to show users on login. Supports HTML.
958
1004
  */
959
- loginPromptHTML?: string,
1005
+ loginPromptHTML?: string | (() => string | void | undefined | Promise<string | void | undefined>) | undefined
960
1006
 
961
1007
  /**
962
1008
  * Remember me days for "Remember Me" checkbox on login page.
@@ -1062,8 +1108,15 @@ export interface AdminForthConfigCustomization extends Omit<AdminForthInputConfi
1062
1108
  userMenu: Array<AdminForthComponentDeclarationFull>,
1063
1109
  header: Array<AdminForthComponentDeclarationFull>,
1064
1110
  sidebar: Array<AdminForthComponentDeclarationFull>,
1111
+ sidebarTop: Array<AdminForthComponentDeclarationFull>,
1065
1112
  everyPageBottom: Array<AdminForthComponentDeclarationFull>,
1066
1113
  },
1114
+
1115
+ customHeadItems?: {
1116
+ tagName: string;
1117
+ attributes: Record<string, string | boolean>;
1118
+ }[];
1119
+
1067
1120
  }
1068
1121
 
1069
1122
  export interface AdminForthConfig extends Omit<AdminForthInputConfig, 'customization' | 'resources'> {
@@ -1111,6 +1164,9 @@ export class Filters {
1111
1164
  static LIKE(field: string, value: any): IAdminForthSingleFilter {
1112
1165
  return { field, operator: AdminForthFilterOperators.LIKE, value };
1113
1166
  }
1167
+ static ILIKE(field: string, value: any): IAdminForthSingleFilter {
1168
+ return { field, operator: AdminForthFilterOperators.ILIKE, value };
1169
+ }
1114
1170
  static AND(
1115
1171
  ...args: (IAdminForthSingleFilter | IAdminForthAndOrFilter | Array<IAdminForthSingleFilter | IAdminForthAndOrFilter>)[]
1116
1172
  ): IAdminForthAndOrFilter {
@@ -1318,9 +1374,13 @@ export interface AdminForthResource extends Omit<AdminForthResourceInput, 'optio
1318
1374
  },
1319
1375
  create?: {
1320
1376
  /**
1377
+ * Should return `ok: true` to continue saving pipeline and allow creating record in database, and `ok: false` to interrupt pipeline and prevent record creation.
1378
+ * If you need to show error on UI, set `error: \<error message\>` in response.
1379
+ *
1321
1380
  * Typical use-cases:
1322
- * - Validate record before saving to database and interrupt execution if validation failed (`allowedActions.create` should be preferred in most cases)
1323
- * - fill-in adminUser as creator of record
1381
+ * - Create record by custom code (return `{ ok: false, newRecordId: <id of created record from custom code> }`)
1382
+ * - 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
1383
+ * - fill-in adminUser as creator of record (set `record.<some field> = x; return \{ ok: true \}`)
1324
1384
  * - Attach additional data to record before saving to database (mostly fillOnCreate should be used instead)
1325
1385
  */
1326
1386
  beforeSave?: Array<BeforeCreateSaveFunction>,
@@ -1471,15 +1531,27 @@ export type ShowInInput = ShowInModernInput | ShowInLegacyInput;
1471
1531
  export type ShowIn = {
1472
1532
  [key in AdminForthResourcePages]: AllowedActionValue
1473
1533
  }
1534
+ export type BackendOnlyInput =
1535
+ | boolean
1536
+ | ((p: {
1537
+ adminUser: AdminUser;
1538
+ resource: AdminForthResource;
1539
+ meta: any;
1540
+ source: ActionCheckSource;
1541
+ adminforth: IAdminForth;
1542
+ }) => boolean | Promise<boolean>);
1543
+
1474
1544
 
1475
- export interface AdminForthResourceColumnInput extends Omit<AdminForthResourceColumnInputCommon, 'showIn'> {
1545
+ export interface AdminForthResourceColumnInput extends Omit<AdminForthResourceColumnInputCommon, 'showIn' | 'backendOnly'> {
1476
1546
  showIn?: ShowInInput,
1477
1547
  foreignResource?: AdminForthForeignResource,
1548
+ backendOnly?: BackendOnlyInput;
1478
1549
  }
1479
1550
 
1480
- export interface AdminForthResourceColumn extends Omit<AdminForthResourceColumnCommon, 'showIn'> {
1551
+ export interface AdminForthResourceColumn extends Omit<AdminForthResourceColumnCommon, 'showIn' | 'backendOnly'> {
1481
1552
  showIn?: ShowIn,
1482
1553
  foreignResource?: AdminForthForeignResource,
1554
+ backendOnly?: BackendOnlyInput;
1483
1555
  }
1484
1556
 
1485
1557
  export interface IWebSocketClient {
@@ -1,4 +1,3 @@
1
-
2
1
  /**
3
2
  * Types that are common for both frontend side (SPA) and backend side (server).
4
3
  */
@@ -95,12 +94,9 @@ export interface AdminForthBulkActionCommon {
95
94
  label: string,
96
95
 
97
96
  /**
98
- * Bulk Action button state 'danger'|success|'active',
99
- * * 'danger' - red button
100
- * * 'success' - green button
101
- * * 'active' - blue button
97
+ * Add custom class
102
98
  **/
103
- state?: 'danger' | 'success' | 'active';
99
+ buttonCustomCssClass?: string;
104
100
 
105
101
  /**
106
102
  * Optional small badge for button which will be displayed in the list view
@@ -122,6 +118,10 @@ export interface AdminForthBulkActionCommon {
122
118
  */
123
119
  successMessage?: string,
124
120
 
121
+ /**
122
+ * Show in three dots dropdown menu in list view.
123
+ */
124
+ showInThreeDotsDropdown?: boolean,
125
125
  }
126
126
 
127
127
  export interface AdminForthFieldComponents {
@@ -263,9 +263,17 @@ export interface AdminForthComponentDeclarationFull {
263
263
  */
264
264
  meta?: any,
265
265
  }
266
+ import { type AdminForthActionInput } from './Back.js'
267
+ export { type AdminForthActionInput } from './Back.js'
266
268
 
267
269
  export type AdminForthComponentDeclaration = AdminForthComponentDeclarationFull | string;
268
270
 
271
+ export type FieldGroup = {
272
+ groupName: string;
273
+ columns: string[];
274
+ noTitle?: boolean;
275
+ };
276
+
269
277
  /**
270
278
  * Resource describes one table or collection in database.
271
279
  * AdminForth generates set of pages for 'list', 'show', 'edit', 'create', 'filter' operations for each resource.
@@ -347,6 +355,11 @@ export interface AdminForthResourceInputCommon {
347
355
  direction: AdminForthSortDirections | string,
348
356
  }
349
357
 
358
+ /*
359
+ * Custom actions list. Actions available in show, edit and create views.
360
+ */
361
+ actions?: AdminForthActionInput[],
362
+
350
363
  /**
351
364
  * Custom bulk actions list. Bulk actions available in list view when user selects multiple records by
352
365
  * using checkboxes.
@@ -374,26 +387,10 @@ export interface AdminForthResourceInputCommon {
374
387
  /**
375
388
  * Allows to make groups of columns in show, create and edit resource pages.
376
389
  */
377
- fieldGroups?: {
378
- groupName: string;
379
- columns: string[];
380
- noTitle?: boolean;
381
- }[];
382
- createFieldGroups?: {
383
- groupName: string;
384
- columns: string[];
385
- noTitle?: boolean;
386
- }[];
387
- editFieldGroups?: {
388
- groupName: string;
389
- columns: string[];
390
- noTitle?: boolean;
391
- }[];
392
- showFieldGroups?: {
393
- groupName: string;
394
- columns: string[];
395
- noTitle?: boolean;
396
- }[];
390
+ fieldGroups?: FieldGroup[];
391
+ createFieldGroups?: FieldGroup[];
392
+ editFieldGroups?: FieldGroup[];
393
+ showFieldGroups?: FieldGroup[];
397
394
 
398
395
  /**
399
396
  * Page size for 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: {
@@ -810,9 +809,6 @@ export interface AdminForthResourceColumnInputCommon {
810
809
  */
811
810
  minLength?: number,
812
811
 
813
- min?: number,
814
- max?: number,
815
-
816
812
  /**
817
813
  * Minimum value that can be entered in this field.
818
814
  */
@@ -878,6 +874,15 @@ export interface AdminForthResourceColumnCommon extends AdminForthResourceColumn
878
874
 
879
875
  editingNote?: { create?: string, edit?: string },
880
876
 
877
+ /**
878
+ * Minimal value stored in this field.
879
+ */
880
+ min?: number,
881
+
882
+ /**
883
+ * Maximum value stored in this field.
884
+ */
885
+ max?: number,
881
886
  }
882
887
 
883
888
  export enum AdminForthMenuTypes {
@@ -1055,16 +1060,19 @@ export interface AdminForthConfigForFrontend {
1055
1060
  usernameFieldName: string,
1056
1061
  loginBackgroundImage: string,
1057
1062
  loginBackgroundPosition: string,
1063
+ removeBackgroundBlendMode: boolean,
1058
1064
  title?: string,
1059
1065
  demoCredentials?: string,
1060
- loginPromptHTML?: string,
1066
+ loginPromptHTML?: string | (() => string | Promise<string> | void | Promise<void> | Promise<undefined>) | undefined
1061
1067
  loginPageInjections: {
1062
1068
  underInputs: Array<AdminForthComponentDeclaration>,
1063
1069
  panelHeader: Array<AdminForthComponentDeclaration>,
1064
1070
  },
1065
1071
  rememberMeDays: number,
1066
1072
  showBrandNameInSidebar: boolean,
1073
+ showBrandLogoInSidebar: boolean,
1067
1074
  brandLogo?: string,
1075
+ singleTheme?: 'light' | 'dark',
1068
1076
  datesFormat: string,
1069
1077
  timeFormat: string,
1070
1078
  auth: any,
@@ -1079,8 +1087,13 @@ export interface AdminForthConfigForFrontend {
1079
1087
  userMenu: Array<AdminForthComponentDeclarationFull>,
1080
1088
  header: Array<AdminForthComponentDeclarationFull>,
1081
1089
  sidebar: Array<AdminForthComponentDeclarationFull>,
1090
+ sidebarTop: Array<AdminForthComponentDeclarationFull>,
1082
1091
  everyPageBottom: Array<AdminForthComponentDeclarationFull>,
1083
- }
1092
+ },
1093
+ customHeadItems?: {
1094
+ tagName: string;
1095
+ attributes: Record<string, string | boolean>;
1096
+ }[],
1084
1097
  }
1085
1098
 
1086
1099
  export interface GetBaseConfigResponse {
@@ -121,6 +121,14 @@ export interface FrontendAPIInterface {
121
121
  clearFilters(): void;
122
122
  }
123
123
 
124
+ show: {
125
+ /**
126
+ * Full refresh the current record on the show page. Loader may be shown during fetching.
127
+ * Fire-and-forget; you don't need to await it.
128
+ */
129
+ refresh(): void;
130
+ }
131
+
124
132
  menu: {
125
133
  /**
126
134
  * Refreshes the badges in the menu, by recalling the badge function for each menu item