adminforth 2.4.0-next.33 → 2.4.0-next.331

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 (177) hide show
  1. package/commands/callTsProxy.js +14 -4
  2. package/commands/createApp/templates/api.ts.hbs +10 -0
  3. package/commands/createApp/templates/custom/tsconfig.json.hbs +2 -3
  4. package/commands/createApp/templates/index.ts.hbs +12 -1
  5. package/commands/createApp/templates/package.json.hbs +1 -1
  6. package/commands/createApp/templates/prisma.config.ts.hbs +8 -0
  7. package/commands/createApp/templates/schema.prisma.hbs +0 -1
  8. package/commands/createApp/utils.js +10 -0
  9. package/commands/createCustomComponent/configLoader.js +17 -4
  10. package/commands/createCustomComponent/main.js +13 -7
  11. package/commands/createCustomComponent/templates/customCrud/beforeActionButtons.vue.hbs +38 -0
  12. package/commands/createCustomComponent/templates/customCrud/saveButton.vue.hbs +28 -0
  13. package/commands/createPlugin/templates/custom/tsconfig.json.hbs +2 -5
  14. package/commands/createPlugin/templates/package.json.hbs +1 -1
  15. package/commands/generateModels.js +30 -22
  16. package/dist/auth.d.ts +9 -1
  17. package/dist/auth.d.ts.map +1 -1
  18. package/dist/auth.js +21 -2
  19. package/dist/auth.js.map +1 -1
  20. package/dist/dataConnectors/baseConnector.d.ts +1 -1
  21. package/dist/dataConnectors/baseConnector.d.ts.map +1 -1
  22. package/dist/dataConnectors/baseConnector.js +70 -18
  23. package/dist/dataConnectors/baseConnector.js.map +1 -1
  24. package/dist/dataConnectors/clickhouse.d.ts.map +1 -1
  25. package/dist/dataConnectors/clickhouse.js +15 -0
  26. package/dist/dataConnectors/clickhouse.js.map +1 -1
  27. package/dist/dataConnectors/mongo.d.ts.map +1 -1
  28. package/dist/dataConnectors/mongo.js +50 -15
  29. package/dist/dataConnectors/mongo.js.map +1 -1
  30. package/dist/dataConnectors/mysql.d.ts.map +1 -1
  31. package/dist/dataConnectors/mysql.js +11 -0
  32. package/dist/dataConnectors/mysql.js.map +1 -1
  33. package/dist/dataConnectors/postgres.d.ts.map +1 -1
  34. package/dist/dataConnectors/postgres.js +43 -14
  35. package/dist/dataConnectors/postgres.js.map +1 -1
  36. package/dist/dataConnectors/sqlite.d.ts.map +1 -1
  37. package/dist/dataConnectors/sqlite.js +11 -0
  38. package/dist/dataConnectors/sqlite.js.map +1 -1
  39. package/dist/index.d.ts +11 -1
  40. package/dist/index.d.ts.map +1 -1
  41. package/dist/index.js +44 -21
  42. package/dist/index.js.map +1 -1
  43. package/dist/modules/codeInjector.d.ts +2 -0
  44. package/dist/modules/codeInjector.d.ts.map +1 -1
  45. package/dist/modules/codeInjector.js +62 -6
  46. package/dist/modules/codeInjector.js.map +1 -1
  47. package/dist/modules/configValidator.d.ts +6 -0
  48. package/dist/modules/configValidator.d.ts.map +1 -1
  49. package/dist/modules/configValidator.js +209 -25
  50. package/dist/modules/configValidator.js.map +1 -1
  51. package/dist/modules/restApi.d.ts +1 -1
  52. package/dist/modules/restApi.d.ts.map +1 -1
  53. package/dist/modules/restApi.js +199 -31
  54. package/dist/modules/restApi.js.map +1 -1
  55. package/dist/modules/styles.d.ts +499 -13
  56. package/dist/modules/styles.d.ts.map +1 -1
  57. package/dist/modules/styles.js +555 -31
  58. package/dist/modules/styles.js.map +1 -1
  59. package/dist/modules/utils.d.ts +7 -15
  60. package/dist/modules/utils.d.ts.map +1 -1
  61. package/dist/modules/utils.js +45 -68
  62. package/dist/modules/utils.js.map +1 -1
  63. package/dist/servers/express.d.ts +5 -0
  64. package/dist/servers/express.d.ts.map +1 -1
  65. package/dist/servers/express.js +40 -1
  66. package/dist/servers/express.js.map +1 -1
  67. package/dist/spa/index.html +1 -1
  68. package/dist/spa/package-lock.json +1208 -708
  69. package/dist/spa/package.json +34 -34
  70. package/dist/spa/src/App.vue +132 -174
  71. package/dist/spa/src/adminforth.ts +41 -17
  72. package/dist/spa/src/afcl/AreaChart.vue +0 -1
  73. package/dist/spa/src/afcl/BarChart.vue +2 -2
  74. package/dist/spa/src/afcl/Button.vue +3 -3
  75. package/dist/spa/src/afcl/ButtonGroup.vue +91 -0
  76. package/dist/spa/src/afcl/Card.vue +25 -0
  77. package/dist/spa/src/afcl/Checkbox.vue +21 -13
  78. package/dist/spa/src/afcl/CountryFlag.vue +4 -1
  79. package/dist/spa/src/{components/CustomDatePicker.vue → afcl/DatePicker.vue} +95 -9
  80. package/dist/spa/src/afcl/Dialog.vue +47 -27
  81. package/dist/spa/src/afcl/Dropzone.vue +145 -48
  82. package/dist/spa/src/afcl/Input.vue +14 -6
  83. package/dist/spa/src/afcl/JsonViewer.vue +25 -0
  84. package/dist/spa/src/afcl/LinkButton.vue +3 -3
  85. package/dist/spa/src/afcl/PieChart.vue +5 -5
  86. package/dist/spa/src/afcl/ProgressBar.vue +7 -7
  87. package/dist/spa/src/afcl/Select.vue +82 -34
  88. package/dist/spa/src/afcl/Skeleton.vue +6 -6
  89. package/dist/spa/src/afcl/Table.vue +313 -75
  90. package/dist/spa/src/afcl/Textarea.vue +31 -0
  91. package/dist/spa/src/afcl/Toggle.vue +32 -0
  92. package/dist/spa/src/afcl/Tooltip.vue +28 -18
  93. package/dist/spa/src/afcl/VerticalTabs.vue +21 -7
  94. package/dist/spa/src/afcl/index.ts +6 -3
  95. package/dist/spa/src/components/AcceptModal.vue +48 -14
  96. package/dist/spa/src/components/Breadcrumbs.vue +5 -5
  97. package/dist/spa/src/components/CallActionWrapper.vue +15 -0
  98. package/dist/spa/src/components/ColumnValueInput.vue +38 -18
  99. package/dist/spa/src/components/ColumnValueInputWrapper.vue +4 -3
  100. package/dist/spa/src/components/CustomDateRangePicker.vue +9 -8
  101. package/dist/spa/src/components/CustomRangePicker.vue +37 -21
  102. package/dist/spa/src/components/ErrorMessage.vue +21 -0
  103. package/dist/spa/src/components/Filters.vue +195 -132
  104. package/dist/spa/src/components/GroupsTable.vue +9 -8
  105. package/dist/spa/src/components/MenuLink.vue +95 -23
  106. package/dist/spa/src/components/ResourceForm.vue +99 -51
  107. package/dist/spa/src/components/ResourceListTable.vue +121 -95
  108. package/dist/spa/src/components/ResourceListTableVirtual.vue +119 -88
  109. package/dist/spa/src/components/ShowTable.vue +21 -15
  110. package/dist/spa/src/components/Sidebar.vue +472 -0
  111. package/dist/spa/src/components/SingleSkeletLoader.vue +6 -6
  112. package/dist/spa/src/components/SkeleteLoader.vue +3 -3
  113. package/dist/spa/src/components/ThreeDotsMenu.vue +84 -15
  114. package/dist/spa/src/components/Toast.vue +40 -29
  115. package/dist/spa/src/components/UserMenuSettingsButton.vue +69 -0
  116. package/dist/spa/src/components/ValueRenderer.vue +44 -17
  117. package/dist/spa/src/controls/BoolToggle.vue +34 -0
  118. package/dist/spa/src/i18n.ts +5 -3
  119. package/dist/spa/src/main.ts +1 -1
  120. package/dist/spa/src/renderers/CompactField.vue +1 -1
  121. package/dist/spa/src/renderers/CompactUUID.vue +1 -1
  122. package/dist/spa/src/router/index.ts +8 -0
  123. package/dist/spa/src/shims-vue.d.ts +5 -0
  124. package/dist/spa/src/spa_types/core.ts +13 -1
  125. package/dist/spa/src/stores/core.ts +15 -1
  126. package/dist/spa/src/stores/filters.ts +33 -2
  127. package/dist/spa/src/stores/modal.ts +6 -1
  128. package/dist/spa/src/stores/toast.ts +22 -3
  129. package/dist/spa/src/types/Back.ts +168 -23
  130. package/dist/spa/src/types/Common.ts +109 -32
  131. package/dist/spa/src/types/FrontendAPI.ts +32 -23
  132. package/dist/spa/src/types/adapters/CaptchaAdapter.ts +34 -0
  133. package/dist/spa/src/types/adapters/EmailAdapter.ts +2 -4
  134. package/dist/spa/src/types/adapters/ImageVisionAdapter.ts +30 -0
  135. package/dist/spa/src/types/adapters/KeyValueAdapter.ts +16 -0
  136. package/dist/spa/src/types/adapters/StorageAdapter.ts +4 -2
  137. package/dist/spa/src/types/adapters/index.ts +3 -0
  138. package/dist/spa/src/utils.ts +291 -11
  139. package/dist/spa/src/views/CreateView.vue +88 -22
  140. package/dist/spa/src/views/EditView.vue +55 -22
  141. package/dist/spa/src/views/ListView.vue +144 -87
  142. package/dist/spa/src/views/LoginView.vue +26 -35
  143. package/dist/spa/src/views/ResourceParent.vue +2 -2
  144. package/dist/spa/src/views/SettingsView.vue +121 -0
  145. package/dist/spa/src/views/ShowView.vue +83 -53
  146. package/dist/spa/src/websocket.ts +6 -1
  147. package/dist/spa/tsconfig.app.json +1 -1
  148. package/dist/spa/vite.config.ts +45 -2
  149. package/dist/types/Back.d.ts +151 -14
  150. package/dist/types/Back.d.ts.map +1 -1
  151. package/dist/types/Back.js +15 -0
  152. package/dist/types/Back.js.map +1 -1
  153. package/dist/types/Common.d.ts +123 -29
  154. package/dist/types/Common.d.ts.map +1 -1
  155. package/dist/types/Common.js.map +1 -1
  156. package/dist/types/FrontendAPI.d.ts +32 -18
  157. package/dist/types/FrontendAPI.d.ts.map +1 -1
  158. package/dist/types/FrontendAPI.js.map +1 -1
  159. package/dist/types/adapters/CaptchaAdapter.d.ts +30 -0
  160. package/dist/types/adapters/CaptchaAdapter.d.ts.map +1 -0
  161. package/dist/types/adapters/CaptchaAdapter.js +5 -0
  162. package/dist/types/adapters/CaptchaAdapter.js.map +1 -0
  163. package/dist/types/adapters/EmailAdapter.d.ts +2 -3
  164. package/dist/types/adapters/EmailAdapter.d.ts.map +1 -1
  165. package/dist/types/adapters/ImageVisionAdapter.d.ts +25 -0
  166. package/dist/types/adapters/ImageVisionAdapter.d.ts.map +1 -0
  167. package/dist/types/adapters/ImageVisionAdapter.js +2 -0
  168. package/dist/types/adapters/ImageVisionAdapter.js.map +1 -0
  169. package/dist/types/adapters/KeyValueAdapter.d.ts +10 -0
  170. package/dist/types/adapters/KeyValueAdapter.d.ts.map +1 -0
  171. package/dist/types/adapters/KeyValueAdapter.js +2 -0
  172. package/dist/types/adapters/KeyValueAdapter.js.map +1 -0
  173. package/dist/types/adapters/StorageAdapter.d.ts +2 -0
  174. package/dist/types/adapters/StorageAdapter.d.ts.map +1 -1
  175. package/dist/types/adapters/index.d.ts +3 -0
  176. package/dist/types/adapters/index.d.ts.map +1 -1
  177. package/package.json +4 -2
@@ -16,6 +16,7 @@ export const useCoreStore = defineStore('core', () => {
16
16
  const record: Ref<any | null> = ref({});
17
17
  const resource: Ref<AdminForthResourceCommon | null> = ref(null);
18
18
  const userData: Ref<UserData | null> = ref(null);
19
+ const isResourceFetching = ref(false);
19
20
 
20
21
  const resourceColumnsWithFilters = computed(() => {
21
22
  if (!resource.value) {
@@ -118,7 +119,7 @@ export const useCoreStore = defineStore('core', () => {
118
119
  item.badge = badge;
119
120
  }
120
121
  });
121
-
122
+ websocket.unsubscribeAll();
122
123
  subscribeToMenuBadges();
123
124
 
124
125
  }
@@ -172,6 +173,7 @@ export const useCoreStore = defineStore('core', () => {
172
173
  // already fetched
173
174
  return;
174
175
  }
176
+ isResourceFetching.value = true;
175
177
  resourceColumnsId.value = resourceId;
176
178
  resourceColumnsError.value = '';
177
179
  const res = await callAdminForthApi({
@@ -188,6 +190,7 @@ export const useCoreStore = defineStore('core', () => {
188
190
  resource.value = res.resource;
189
191
  resourceOptions.value = res.resource.options;
190
192
  }
193
+ isResourceFetching.value = false;
191
194
  }
192
195
 
193
196
  async function getPublicConfig() {
@@ -195,9 +198,18 @@ export const useCoreStore = defineStore('core', () => {
195
198
  path: '/get_public_config',
196
199
  method: 'GET',
197
200
  });
201
+ console.log('📦 getPublicConfig', res);
198
202
  config.value = {...config.value, ...res};
199
203
  }
200
204
 
205
+ async function getLoginFormConfig() {
206
+ const res = await callAdminForthApi({
207
+ path: '/get_login_form_config',
208
+ method: 'GET',
209
+ });
210
+ console.log('📦 getLoginFormConfig', res);
211
+ config.value = {...config.value, ...res};
212
+ }
201
213
 
202
214
  const username = computed(() => {
203
215
  const usernameField = config.value?.usernameField;
@@ -218,6 +230,7 @@ export const useCoreStore = defineStore('core', () => {
218
230
  userFullname,
219
231
  getPublicConfig,
220
232
  fetchMenuAndResource,
233
+ getLoginFormConfig,
221
234
  fetchRecord,
222
235
  record,
223
236
  fetchResourceFull,
@@ -231,5 +244,6 @@ export const useCoreStore = defineStore('core', () => {
231
244
  fetchMenuBadges,
232
245
  resetAdminUser,
233
246
  resetResource,
247
+ isResourceFetching,
234
248
  }
235
249
  })
@@ -1,9 +1,11 @@
1
- import { ref, type Ref } from 'vue';
1
+ import { ref, computed, type Ref } from 'vue';
2
2
  import { defineStore } from 'pinia';
3
+ import { useCoreStore } from './core';
3
4
 
4
5
  export const useFiltersStore = defineStore('filters', () => {
5
6
  const filters: Ref<any[]> = ref([]);
6
7
  const sort: Ref<any> = ref({});
8
+ const coreStore = useCoreStore();
7
9
 
8
10
  const setSort = (s: any) => {
9
11
  sort.value = s;
@@ -20,8 +22,37 @@ export const useFiltersStore = defineStore('filters', () => {
20
22
  const getFilters = () => {
21
23
  return filters.value;
22
24
  }
25
+ const clearFilter = (fieldName: string) => {
26
+ filters.value = filters.value.filter(f => f.field !== fieldName);
27
+ }
23
28
  const clearFilters = () => {
24
29
  filters.value = [];
25
30
  }
26
- return {setFilter, getFilters, clearFilters, filters, setFilters, setSort, getSort}
31
+
32
+ const shouldFilterBeHidden = (fieldName: string) => {
33
+ if (coreStore.resource?.columns) {
34
+ const column = coreStore.resource.columns.find((col: any) => col.name === fieldName);
35
+ if (column?.showIn?.filter !== true) {
36
+ return true;
37
+ }
38
+ }
39
+ return false;
40
+ }
41
+
42
+ const visibleFiltersCount = computed(() => {
43
+ return filters.value.filter(f => !shouldFilterBeHidden(f.field)).length;
44
+ });
45
+
46
+ return {
47
+ setFilter,
48
+ getFilters,
49
+ clearFilters,
50
+ filters,
51
+ setFilters,
52
+ setSort,
53
+ getSort,
54
+ visibleFiltersCount,
55
+ shouldFilterBeHidden,
56
+ clearFilter
57
+ }
27
58
  })
@@ -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;
@@ -12,19 +12,38 @@ export const useToastStore = defineStore('toast', () => {
12
12
  watch(route, () => {
13
13
  // on route change clear all toasts older then 5 seconds
14
14
  const now = +new Date();
15
- toasts.value = toasts.value.filter((t) => now - t.createdAt < 5000);
15
+ toasts.value = toasts.value.filter((t) => t?.timeout === 'unlimited' || now - t.createdAt < 5000);
16
16
  });
17
17
 
18
- const addToast = (toast: { message: string; variant: string }) => {
18
+ const addToast = (toast: {
19
+ message?: string;
20
+ messageHtml?: string;
21
+ variant: string;
22
+ timeout?: number | 'unlimited';
23
+ buttons?: { value: any; label: string }[];
24
+ onResolve?: (value?: any) => void;
25
+ }): string => {
19
26
  const toastId = uuid();
20
27
  toasts.value.push({
21
28
  ...toast,
22
29
  id: toastId,
23
30
  createdAt: +new Date(),
24
31
  });
32
+ return toastId;
25
33
  };
26
34
  const removeToast = (toast: { id: string }) => {
27
35
  toasts.value = toasts.value.filter((t) => t.id !== toast.id);
28
36
  };
29
- return { toasts, addToast, removeToast };
37
+
38
+ const resolveToast = (toastId: string, value?: any) => {
39
+ const t = toasts.value.find((x) => x.id === toastId);
40
+ try {
41
+ t?.onResolve?.(value);
42
+ } catch {
43
+ // no-op
44
+ }
45
+ toasts.value = toasts.value.filter((x) => x.id !== toastId);
46
+ };
47
+
48
+ return { toasts, addToast, removeToast, resolveToast };
30
49
  });
@@ -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 {
@@ -22,12 +22,13 @@ export interface ICodeInjector {
22
22
  devServerPort: number;
23
23
 
24
24
  getServeDir(): string;
25
-
25
+ registerCustomComponent(filePath: string): void;
26
26
  spaTmpPath(): string;
27
27
  }
28
28
 
29
29
  export interface IConfigValidator {
30
30
  validateConfig(): void;
31
+ validateAfterPluginsActivation(): void;
31
32
  postProcessAfterDiscover(resource: AdminForthResource): void;
32
33
  }
33
34
 
@@ -92,19 +93,36 @@ export interface IExpressHttpServer extends IHttpServer {
92
93
  * Adds adminUser to request object if user is authorized. Drops request with 401 status if user is not authorized.
93
94
  * @param callable : Function which will be called if user is authorized.
94
95
  *
95
- * Example:
96
96
  *
97
+ * @example
97
98
  * ```ts
98
- * expressApp.get('/myApi', authorize((req, res) => \{
99
+ * expressApp.get('/myApi', authorize((req, res) => {
99
100
  * console.log('User is authorized', req.adminUser);
100
- * res.json(\{ message: 'Hello World' \});
101
- * \}));
102
- * ``
101
+ * res.json({ message: 'Hello World' });
102
+ * }));
103
+ * ```
103
104
  *
104
- */
105
+ */
105
106
  authorize(callable: Function): void;
106
107
  }
107
108
 
109
+ export interface ITranslateFunction {
110
+ (
111
+ msg: string,
112
+ category: string,
113
+ params: any,
114
+ pluralizationNumber?: number
115
+ ): Promise<string>;
116
+ }
117
+
118
+ // Omit <Request, 'param'> is used to remove 'param' method from Request type for correct docs generation
119
+ export interface IAdminUserExpressRequest extends Omit<Request, 'protocol' | 'param' | 'unshift'> {
120
+ adminUser: AdminUser;
121
+ }
122
+
123
+ export interface ITranslateExpressRequest extends Omit<Request, 'protocol' | 'param' | 'unshift'> {
124
+ tr: ITranslateFunction;
125
+ }
108
126
 
109
127
  export interface IAdminForthSingleFilter {
110
128
  field?: string;
@@ -113,7 +131,9 @@ export interface IAdminForthSingleFilter {
113
131
  | AdminForthFilterOperators.LTE | AdminForthFilterOperators.LIKE | AdminForthFilterOperators.ILIKE
114
132
  | AdminForthFilterOperators.IN | AdminForthFilterOperators.NIN;
115
133
  value?: any;
134
+ rightField?: string;
116
135
  insecureRawSQL?: string;
136
+ insecureRawNoSQL?: any;
117
137
  }
118
138
  export interface IAdminForthAndOrFilter {
119
139
  operator: AdminForthFilterOperators.AND | AdminForthFilterOperators.OR;
@@ -287,6 +307,10 @@ export interface IAdminForthAuth {
287
307
 
288
308
  removeCustomCookie({response, name}: {response: any, name: string}): void;
289
309
 
310
+ setCustomCookie({response, payload}: {response: any, payload: {name: string, value: string, expiry: number, expirySeconds: number, httpOnly: boolean}}): void;
311
+
312
+ getCustomCookie({cookies, name}: {cookies: {key: string, value: string}[], name: string}): string | null;
313
+
290
314
  setAuthCookie({expireInDays, response, username, pk,}: {expireInDays?: number, response: any, username: string, pk: string}): void;
291
315
 
292
316
  removeAuthCookie(response: any): void;
@@ -336,10 +360,11 @@ export interface IAdminForth {
336
360
 
337
361
  createResourceRecord(
338
362
  params: { resource: AdminForthResource, record: any, adminUser: AdminUser, extra?: HttpExtra }
339
- ): Promise<{ error?: string, createdRecord?: any }>;
363
+ ): Promise<{ error?: string, createdRecord?: any, newRecordId?: any }>;
340
364
 
341
365
  updateResourceRecord(
342
- params: { resource: AdminForthResource, recordId: any, record: any, oldRecord: any, adminUser: AdminUser, extra?: HttpExtra }
366
+ params: { resource: AdminForthResource, recordId: any, record: any, oldRecord: any, adminUser: AdminUser, extra?: HttpExtra, updates?: never }
367
+ | { resource: AdminForthResource, recordId: any, record?: never, oldRecord: any, adminUser: AdminUser, extra?: HttpExtra, updates: any }
343
368
  ): Promise<{ error?: string }>;
344
369
 
345
370
  deleteResourceRecord(
@@ -474,7 +499,7 @@ export type BeforeDataSourceRequestFunction = (params: {
474
499
  requestUrl: string,
475
500
  },
476
501
  adminforth: IAdminForth,
477
- }) => Promise<{ok: boolean, error?: string}>;
502
+ }) => Promise<{ok: boolean, error?: string, newRecordId?: string}>;
478
503
 
479
504
  /**
480
505
  * Modify response to change how data is returned after fetching from database.
@@ -525,7 +550,7 @@ export type BeforeEditSaveFunction = (params: {
525
550
  oldRecord: any,
526
551
  adminforth: IAdminForth,
527
552
  extra?: HttpExtra,
528
- }) => Promise<{ok: boolean, error?: string}>;
553
+ }) => Promise<{ok: boolean, error?: string | null}>;
529
554
 
530
555
 
531
556
 
@@ -535,7 +560,7 @@ export type BeforeCreateSaveFunction = (params: {
535
560
  record: any,
536
561
  adminforth: IAdminForth,
537
562
  extra?: HttpExtra,
538
- }) => Promise<{ok: boolean, error?: string}>;
563
+ }) => Promise<{ok: boolean, error?: string | null, newRecordId?: string}>;
539
564
 
540
565
  export type AfterCreateSaveFunction = (params: {
541
566
  resource: AdminForthResource,
@@ -543,6 +568,7 @@ export type AfterCreateSaveFunction = (params: {
543
568
  adminUser: AdminUser,
544
569
  record: any,
545
570
  adminforth: IAdminForth,
571
+ recordWithVirtualColumns?: any,
546
572
  extra?: HttpExtra,
547
573
  }) => Promise<{ok: boolean, error?: string}>;
548
574
 
@@ -579,6 +605,7 @@ export type BeforeLoginConfirmationFunction = (params?: {
579
605
  response: IAdminForthHttpResponse,
580
606
  adminforth: IAdminForth,
581
607
  extra?: HttpExtra,
608
+ rememberMeDays?: number,
582
609
  }) => Promise<{
583
610
  error?: string,
584
611
  body: {
@@ -587,6 +614,19 @@ export type BeforeLoginConfirmationFunction = (params?: {
587
614
  }
588
615
  }>;
589
616
 
617
+ /**
618
+ * Allow to make extra authorization
619
+ */
620
+ export type AdminUserAuthorizeFunction = ((params?: {
621
+ adminUser: AdminUser,
622
+ response: IAdminForthHttpResponse,
623
+ adminforth: IAdminForth,
624
+ extra?: HttpExtra,
625
+ }) => Promise<{
626
+ error?: string,
627
+ allowed?: boolean,
628
+ }>);
629
+
590
630
 
591
631
  /**
592
632
  * Data source describes database connection which will be used to fetch data for resources.
@@ -619,12 +659,23 @@ interface AdminForthInputConfigCustomization {
619
659
  */
620
660
  brandName?: string,
621
661
 
662
+ /**
663
+ * Whether to use single theme for the app
664
+ */
665
+ singleTheme?: 'light' | 'dark',
666
+
622
667
  /**
623
668
  * Whether to show brand name in sidebar
624
669
  * default is true
625
670
  */
626
671
  showBrandNameInSidebar?: boolean,
627
672
 
673
+ /**
674
+ * Whether to show brand logo in sidebar
675
+ * default is true
676
+ */
677
+ showBrandLogoInSidebar?: boolean,
678
+
628
679
  /**
629
680
  * Path to your app logo
630
681
  *
@@ -638,6 +689,23 @@ interface AdminForthInputConfigCustomization {
638
689
  */
639
690
  brandLogo?: string,
640
691
 
692
+
693
+ /**
694
+ * Path to your app logo for icon only sidebar
695
+ *
696
+ * Example:
697
+ * Place file `logo.svg` to `./custom` folder and set this option:
698
+ *
699
+ */
700
+ iconOnlySidebar?: {
701
+ logo?: string,
702
+ enabled?: boolean,
703
+ /**
704
+ * Width of expanded sidebar (default: '16.5rem')
705
+ */
706
+ expandedSidebarWidth?: string,
707
+ },
708
+
641
709
  /**
642
710
  * Path to your app favicon
643
711
  *
@@ -747,6 +815,7 @@ interface AdminForthInputConfigCustomization {
747
815
  */
748
816
  loginPageInjections?: {
749
817
  underInputs?: AdminForthComponentDeclaration | Array<AdminForthComponentDeclaration>,
818
+ underLoginButton?: AdminForthComponentDeclaration | Array<AdminForthComponentDeclaration>,
750
819
  panelHeader?: AdminForthComponentDeclaration | Array<AdminForthComponentDeclaration>,
751
820
  }
752
821
 
@@ -757,8 +826,20 @@ interface AdminForthInputConfigCustomization {
757
826
  userMenu?: AdminForthComponentDeclaration | Array<AdminForthComponentDeclaration>,
758
827
  header?: AdminForthComponentDeclaration | Array<AdminForthComponentDeclaration>,
759
828
  sidebar?: AdminForthComponentDeclaration | Array<AdminForthComponentDeclaration>,
829
+ sidebarTop?: AdminForthComponentDeclaration | Array<AdminForthComponentDeclaration>,
760
830
  everyPageBottom?: AdminForthComponentDeclaration | Array<AdminForthComponentDeclaration>,
761
831
  }
832
+
833
+ /**
834
+ * Allows adding custom elements (e.g., &lt;link&gt;, &lt;script&gt;, &lt;meta&gt;) to the &lt;head&gt; of the HTML document.
835
+ * Each item must include a tag name and a set of attributes.
836
+ */
837
+ customHeadItems?: {
838
+ tagName: string;
839
+ attributes: Record<string, string | boolean>;
840
+ innerCode?: string;
841
+ }[];
842
+
762
843
  }
763
844
 
764
845
  export interface AdminForthActionInput {
@@ -787,6 +868,7 @@ export interface AdminForthActionInput {
787
868
  }>;
788
869
  icon?: string;
789
870
  id?: string;
871
+ customComponent?: AdminForthComponentDeclaration;
790
872
  }
791
873
 
792
874
  export interface AdminForthResourceInput extends Omit<NonNullable<AdminForthResourceInputCommon>, 'columns' | 'hooks' | 'options'> {
@@ -935,12 +1017,24 @@ export interface AdminForthInputConfig {
935
1017
  */
936
1018
  loginBackgroundPosition?: 'over' | '1/2' | '1/3' | '2/3' | '3/4' | '2/5' | '3/5',
937
1019
 
1020
+ /**
1021
+ * If true, background blend mode will be removed from login background image when position is 'over'
1022
+ *
1023
+ * Default: false
1024
+ */
1025
+ removeBackgroundBlendMode?: boolean,
1026
+
938
1027
  /**
939
1028
  * Function or functions which will be called before user try to login.
940
1029
  * Each function will resive User object as an argument
941
1030
  */
942
1031
  beforeLoginConfirmation?: BeforeLoginConfirmationFunction | Array<BeforeLoginConfirmationFunction>,
943
1032
 
1033
+ /**
1034
+ * Array of functions which will be called before any request to AdminForth API.
1035
+ */
1036
+ adminUserAuthorize?: AdminUserAuthorizeFunction | Array<AdminUserAuthorizeFunction>,
1037
+
944
1038
  /**
945
1039
  * Optionally if your users table has a field(column) with full name, you can set it here.
946
1040
  * This field will be used to display user name in the top right corner of the admin panel.
@@ -956,7 +1050,7 @@ export interface AdminForthInputConfig {
956
1050
  /**
957
1051
  * Any prompt to show users on login. Supports HTML.
958
1052
  */
959
- loginPromptHTML?: string,
1053
+ loginPromptHTML?: string | (() => string | void | undefined | Promise<string | void | undefined>) | undefined
960
1054
 
961
1055
  /**
962
1056
  * Remember me days for "Remember Me" checkbox on login page.
@@ -988,6 +1082,17 @@ export interface AdminForthInputConfig {
988
1082
  * If you are using Cloudflare, set this to 'CF-Connecting-IP'. Case-insensitive.
989
1083
  */
990
1084
  clientIpHeader?: string,
1085
+
1086
+ /**
1087
+ * Add custom page to the settings page
1088
+ */
1089
+ userMenuSettingsPages: {
1090
+ icon?: string,
1091
+ pageLabel: string,
1092
+ slug?: string,
1093
+ component: string,
1094
+ isVisible?: (adminUser: AdminUser) => boolean,
1095
+ }[],
991
1096
  },
992
1097
 
993
1098
  /**
@@ -1055,6 +1160,7 @@ export interface AdminForthConfigCustomization extends Omit<AdminForthInputConfi
1055
1160
 
1056
1161
  loginPageInjections: {
1057
1162
  underInputs: Array<AdminForthComponentDeclarationFull>,
1163
+ underLoginButton?: AdminForthComponentDeclaration | Array<AdminForthComponentDeclaration>,
1058
1164
  panelHeader: Array<AdminForthComponentDeclarationFull>,
1059
1165
  },
1060
1166
 
@@ -1062,8 +1168,16 @@ export interface AdminForthConfigCustomization extends Omit<AdminForthInputConfi
1062
1168
  userMenu: Array<AdminForthComponentDeclarationFull>,
1063
1169
  header: Array<AdminForthComponentDeclarationFull>,
1064
1170
  sidebar: Array<AdminForthComponentDeclarationFull>,
1171
+ sidebarTop: Array<AdminForthComponentDeclarationFull>,
1065
1172
  everyPageBottom: Array<AdminForthComponentDeclarationFull>,
1066
1173
  },
1174
+
1175
+ customHeadItems?: {
1176
+ tagName: string;
1177
+ attributes: Record<string, string | boolean>;
1178
+ innerCode?: string;
1179
+ }[];
1180
+
1067
1181
  }
1068
1182
 
1069
1183
  export interface AdminForthConfig extends Omit<AdminForthInputConfig, 'customization' | 'resources'> {
@@ -1111,6 +1225,21 @@ export class Filters {
1111
1225
  static LIKE(field: string, value: any): IAdminForthSingleFilter {
1112
1226
  return { field, operator: AdminForthFilterOperators.LIKE, value };
1113
1227
  }
1228
+ static ILIKE(field: string, value: any): IAdminForthSingleFilter {
1229
+ return { field, operator: AdminForthFilterOperators.ILIKE, value };
1230
+ }
1231
+ static GT_FIELD(leftField: string, rightField: string): IAdminForthSingleFilter {
1232
+ return { field: leftField, operator: AdminForthFilterOperators.GT, rightField };
1233
+ }
1234
+ static GTE_FIELD(leftField: string, rightField: string): IAdminForthSingleFilter {
1235
+ return { field: leftField, operator: AdminForthFilterOperators.GTE, rightField };
1236
+ }
1237
+ static LT_FIELD(leftField: string, rightField: string): IAdminForthSingleFilter {
1238
+ return { field: leftField, operator: AdminForthFilterOperators.LT, rightField };
1239
+ }
1240
+ static LTE_FIELD(leftField: string, rightField: string): IAdminForthSingleFilter {
1241
+ return { field: leftField, operator: AdminForthFilterOperators.LTE, rightField };
1242
+ }
1114
1243
  static AND(
1115
1244
  ...args: (IAdminForthSingleFilter | IAdminForthAndOrFilter | Array<IAdminForthSingleFilter | IAdminForthAndOrFilter>)[]
1116
1245
  ): IAdminForthAndOrFilter {
@@ -1318,9 +1447,13 @@ export interface AdminForthResource extends Omit<AdminForthResourceInput, 'optio
1318
1447
  },
1319
1448
  create?: {
1320
1449
  /**
1450
+ * Should return `ok: true` to continue saving pipeline and allow creating record in database, and `ok: false` to interrupt pipeline and prevent record creation.
1451
+ * If you need to show error on UI, set `error: \<error message\>` in response.
1452
+ *
1321
1453
  * 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
1454
+ * - Create record by custom code (return `{ ok: false, newRecordId: <id of created record from custom code> }`)
1455
+ * - 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
1456
+ * - fill-in adminUser as creator of record (set `record.<some field> = x; return \{ ok: true \}`)
1324
1457
  * - Attach additional data to record before saving to database (mostly fillOnCreate should be used instead)
1325
1458
  */
1326
1459
  beforeSave?: Array<BeforeCreateSaveFunction>,
@@ -1471,15 +1604,27 @@ export type ShowInInput = ShowInModernInput | ShowInLegacyInput;
1471
1604
  export type ShowIn = {
1472
1605
  [key in AdminForthResourcePages]: AllowedActionValue
1473
1606
  }
1607
+ export type BackendOnlyInput =
1608
+ | boolean
1609
+ | ((p: {
1610
+ adminUser: AdminUser;
1611
+ resource: AdminForthResource;
1612
+ meta: any;
1613
+ source: ActionCheckSource;
1614
+ adminforth: IAdminForth;
1615
+ }) => boolean | Promise<boolean>);
1616
+
1474
1617
 
1475
- export interface AdminForthResourceColumnInput extends Omit<AdminForthResourceColumnInputCommon, 'showIn'> {
1618
+ export interface AdminForthResourceColumnInput extends Omit<AdminForthResourceColumnInputCommon, 'showIn' | 'backendOnly'> {
1476
1619
  showIn?: ShowInInput,
1477
1620
  foreignResource?: AdminForthForeignResource,
1621
+ backendOnly?: BackendOnlyInput;
1478
1622
  }
1479
1623
 
1480
- export interface AdminForthResourceColumn extends Omit<AdminForthResourceColumnCommon, 'showIn'> {
1624
+ export interface AdminForthResourceColumn extends Omit<AdminForthResourceColumnCommon, 'showIn' | 'backendOnly'> {
1481
1625
  showIn?: ShowIn,
1482
1626
  foreignResource?: AdminForthForeignResource,
1627
+ backendOnly?: BackendOnlyInput;
1483
1628
  }
1484
1629
 
1485
1630
  export interface IWebSocketClient {