adminforth 1.1.31 → 1.1.33

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 (105) hide show
  1. package/dist/index.js +9 -0
  2. package/dist/modules/codeInjector.js +19 -7
  3. package/dist/modules/styles.js +2 -2
  4. package/dist/spa/index.html +4 -4
  5. package/dist/spa/package-lock.json +206 -5
  6. package/dist/spa/package.json +8 -2
  7. package/dist/spa/src/App.vue +171 -72
  8. package/dist/spa/src/assets/logo.svg +19 -1
  9. package/dist/spa/src/components/AcceptModal.vue +3 -10
  10. package/dist/spa/src/components/BreadcrumbsWithButtons.vue +1 -1
  11. package/dist/spa/src/components/CustomDatePicker.vue +19 -6
  12. package/dist/spa/src/components/CustomDateRangePicker.vue +14 -5
  13. package/dist/spa/src/components/Dropdown.vue +19 -5
  14. package/dist/spa/src/components/Filters.vue +91 -37
  15. package/dist/spa/src/components/MenuLink.vue +2 -2
  16. package/dist/spa/src/components/ResourceForm.vue +163 -103
  17. package/dist/spa/src/components/ValueRenderer.vue +22 -6
  18. package/dist/spa/src/main.ts +1 -1
  19. package/dist/spa/src/router/index.ts +30 -19
  20. package/dist/spa/src/stores/core.ts +65 -59
  21. package/dist/spa/src/stores/modal.ts +13 -3
  22. package/dist/spa/src/utils.ts +65 -8
  23. package/dist/spa/src/views/CreateView.vue +76 -16
  24. package/dist/spa/src/views/EditView.vue +76 -14
  25. package/dist/spa/src/views/ListView.vue +88 -360
  26. package/dist/spa/src/views/LoginView.vue +17 -5
  27. package/dist/spa/src/views/ResourceParent.vue +1 -1
  28. package/dist/spa/src/views/ShowView.vue +114 -19
  29. package/dist/spa/tailwind.config.js +5 -2
  30. package/dist/spa/vite.config.ts +6 -0
  31. package/index.ts +11 -0
  32. package/modules/codeInjector.ts +19 -1
  33. package/modules/styles.ts +2 -2
  34. package/package.json +1 -1
  35. package/spa/index.html +1 -1
  36. package/spa/src/App.vue +3 -2
  37. package/spa/src/components/ResourceForm.vue +1 -1
  38. package/types/AdminForthConfig.ts +15 -0
  39. package/dist/plugins/AccessControl/index.js +0 -13
  40. package/dist/plugins/AccessControl/types.js +0 -1
  41. package/dist/plugins/plugins/ForeignInlineListPlugin/custom/InlineList.vue +0 -248
  42. package/dist/spa/public/favicon.ico +0 -0
  43. package/dist/spa/spa/.eslintrc.cjs +0 -14
  44. package/dist/spa/spa/.vscode/extensions.json +0 -6
  45. package/dist/spa/spa/README.md +0 -39
  46. package/dist/spa/spa/env.d.ts +0 -1
  47. package/dist/spa/spa/index.html +0 -23
  48. package/dist/spa/spa/package-lock.json +0 -4359
  49. package/dist/spa/spa/package.json +0 -47
  50. package/dist/spa/spa/postcss.config.js +0 -6
  51. package/dist/spa/spa/public/favicon.ico +0 -0
  52. package/dist/spa/spa/src/App.vue +0 -270
  53. package/dist/spa/spa/src/assets/base.css +0 -0
  54. package/dist/spa/spa/src/assets/logo.svg +0 -19
  55. package/dist/spa/spa/src/components/AcceptModal.vue +0 -45
  56. package/dist/spa/spa/src/components/Breadcrumbs.vue +0 -40
  57. package/dist/spa/spa/src/components/BreadcrumbsWithButtons.vue +0 -26
  58. package/dist/spa/spa/src/components/CustomDatePicker.vue +0 -174
  59. package/dist/spa/spa/src/components/CustomDateRangePicker.vue +0 -218
  60. package/dist/spa/spa/src/components/Dropdown.vue +0 -168
  61. package/dist/spa/spa/src/components/Filters.vue +0 -211
  62. package/dist/spa/spa/src/components/HelloWorld.vue +0 -17
  63. package/dist/spa/spa/src/components/MenuLink.vue +0 -24
  64. package/dist/spa/spa/src/components/ResourceForm.vue +0 -265
  65. package/dist/spa/spa/src/components/ShowTableItem.vue +0 -14
  66. package/dist/spa/spa/src/components/SingleSkeletLoader.vue +0 -13
  67. package/dist/spa/spa/src/components/ValueRenderer.vue +0 -60
  68. package/dist/spa/spa/src/components/icons/IconCalendar.vue +0 -5
  69. package/dist/spa/spa/src/components/icons/IconCommunity.vue +0 -7
  70. package/dist/spa/spa/src/components/icons/IconDocumentation.vue +0 -7
  71. package/dist/spa/spa/src/components/icons/IconEcosystem.vue +0 -7
  72. package/dist/spa/spa/src/components/icons/IconSupport.vue +0 -7
  73. package/dist/spa/spa/src/components/icons/IconTime.vue +0 -5
  74. package/dist/spa/spa/src/components/icons/IconTooling.vue +0 -19
  75. package/dist/spa/spa/src/index.scss +0 -26
  76. package/dist/spa/spa/src/main.ts +0 -18
  77. package/dist/spa/spa/src/router/index.ts +0 -64
  78. package/dist/spa/spa/src/stores/core.ts +0 -144
  79. package/dist/spa/spa/src/stores/modal.ts +0 -48
  80. package/dist/spa/spa/src/utils.ts +0 -101
  81. package/dist/spa/spa/src/views/CreateView.vue +0 -163
  82. package/dist/spa/spa/src/views/EditView.vue +0 -165
  83. package/dist/spa/spa/src/views/HomeView.vue +0 -8
  84. package/dist/spa/spa/src/views/ListView.vue +0 -257
  85. package/dist/spa/spa/src/views/LoginView.vue +0 -134
  86. package/dist/spa/spa/src/views/ResourceParent.vue +0 -18
  87. package/dist/spa/spa/src/views/ShowView.vue +0 -188
  88. package/dist/spa/spa/tailwind.config.js +0 -15
  89. package/dist/spa/spa/tsconfig.app.json +0 -14
  90. package/dist/spa/spa/tsconfig.json +0 -11
  91. package/dist/spa/spa/tsconfig.node.json +0 -19
  92. package/dist/spa/spa/vite.config.ts +0 -49
  93. package/dist/spa/src/views/HomeView.vue +0 -8
  94. package/dist/types.js +0 -30
  95. /package/dist/spa/{spa/public → public/assets}/favicon.png +0 -0
  96. /package/dist/spa/{spa/src → src}/components/CustomRangePicker.vue +0 -0
  97. /package/dist/spa/{spa/src → src}/components/ResourceListTable.vue +0 -0
  98. /package/dist/spa/{spa/src → src}/components/Toast.vue +0 -0
  99. /package/dist/spa/{spa/src → src}/composables/useFrontendApi.ts +0 -0
  100. /package/dist/spa/{spa/src → src}/composables/useStores.ts +0 -0
  101. /package/dist/spa/{spa/src → src}/spa_types/core.ts +0 -0
  102. /package/dist/spa/{spa/src → src}/stores/filters.ts +0 -0
  103. /package/dist/spa/{spa/src → src}/stores/toast.ts +0 -0
  104. /package/dist/spa/{spa/src → src}/stores/user.ts +0 -0
  105. /package/spa/public/{favicon.png → assets/favicon.png} +0 -0
@@ -1,23 +1,24 @@
1
1
  import { createRouter, createWebHistory } from 'vue-router'
2
- import HomeView from '../views/HomeView.vue'
3
2
  import ResourceParent from '@/views/ResourceParent.vue'
4
- import ListView from '@/views/ListView.vue'
5
- import ShowView from '@/views/ShowView.vue'
6
- import EditView from '@/views/EditView.vue'
7
- import CreateView from '@/views/CreateView.vue'
3
+ import { useUserStore } from '@/stores/user'
4
+ /* IMPORTANT:ADMINFORTH ROUTES IMPORTS */
8
5
 
9
6
  const router = createRouter({
10
7
  history: createWebHistory(import.meta.env.BASE_URL),
11
8
  routes: [
12
- {
13
- path: '/',
14
- name: 'home',
15
- component: HomeView
16
- },
17
9
  {
18
10
  path: '/login',
19
11
  name: 'login',
20
- component: () => import('@/views/LoginView.vue')
12
+ component: () => import('@/views/LoginView.vue'),
13
+ meta: { title: 'login' },
14
+ beforeEnter: async (to, from, next) => {
15
+ const userStore = useUserStore()
16
+ if(localStorage.getItem('isAuthorized') === 'true'){
17
+ next({name: 'home'})
18
+ } else {
19
+ next()
20
+ }
21
+ }
21
22
  },
22
23
  {
23
24
  path: '/resource/:resourceId',
@@ -26,23 +27,29 @@ const router = createRouter({
26
27
  children: [
27
28
  {
28
29
  path: '',
29
- component: ListView,
30
- name: 'resource-list'
30
+ component: () => import('@/views/ListView.vue'),
31
+ name: 'resource-list',
32
+ meta: { title: 'list',type: 'list' }
31
33
  },
32
34
  {
33
35
  path: 'show/:primaryKey',
34
- component: ShowView,
35
- name: 'resource-show'
36
+ component: () => import('@/views/ShowView.vue'),
37
+ name: 'resource-show',
38
+ meta: { title: 'show', type: 'show'}
39
+
36
40
  },
37
41
  {
38
42
  path: 'edit/:primaryKey',
39
- component: EditView,
40
- name: 'resource-edit'
43
+ component: () => import('@/views/EditView.vue'),
44
+ name: 'resource-edit',
45
+ meta: { title: 'edit', type: 'edit'}
41
46
  },
42
47
  {
43
48
  path: 'create',
44
- component: CreateView,
45
- name: 'resource-create'
49
+ component: () => import('@/views/CreateView.vue'),
50
+ name: 'resource-create',
51
+ meta: { title: 'create', type: 'create'}
52
+
46
53
  },
47
54
  ]
48
55
  },
@@ -50,4 +57,8 @@ const router = createRouter({
50
57
  ]
51
58
  })
52
59
 
60
+
61
+
62
+
63
+
53
64
  export default router
@@ -1,96 +1,105 @@
1
1
  import { ref, computed } from 'vue'
2
2
  import { defineStore } from 'pinia'
3
3
  import { callAdminForthApi } from '@/utils';
4
- import router from '@/router';
5
-
6
- async function findHomepage(menu) {
7
- for (const item of menu) {
8
- if (item.homepage) {
9
- return item;
10
- }
11
- if (item.children) {
12
- const res = findHomepage(item.children);
13
- if (res) {
14
- return res;
15
- }
16
- }
17
- }
18
- return null;
19
- }
4
+ import type { AdminForthResource, AdminForthResourceColumn } from '@/types/AdminForthConfig';
5
+ import type { Ref } from 'vue'
20
6
 
21
7
  export const useCoreStore = defineStore('core', () => {
22
- const resourceById = ref([]);
8
+ const resourceById: Ref<Object> = ref({});
23
9
  const menu = ref([]);
24
10
  const config = ref({});
25
- const record = ref({});
26
- const resourceColumns = ref(null);
11
+ const record: Ref<any | null> = ref({});
12
+ const resource: Ref<AdminForthResource | null> = ref(null);
13
+
14
+ const resourceColumnsWithFilters = computed(() => {
15
+ if (!resource.value) {
16
+ return [];
17
+ }
18
+ return resource.value.columns.filter((col: AdminForthResourceColumn) => col.showIn?.includes('filter'));
19
+ })
20
+
21
+ const resourceOptions = ref(null);
27
22
  const resourceColumnsError = ref('');
28
23
  const resourceColumnsId = ref(null);
29
- const user = ref(null);
24
+ const adminUser = ref(null);
30
25
 
31
26
  async function fetchMenuAndResource() {
32
27
  const resp = await callAdminForthApi({
33
28
  path: '/get_base_config',
34
29
  method: 'GET',
35
30
  });
31
+ if(!resp){
32
+ return
33
+ }
36
34
  menu.value = resp.menu;
37
- resourceById.value = resp.resources.reduce((acc, resource) => {
35
+ resourceById.value = resp.resources.reduce((acc: Object, resource: AdminForthResource) => {
38
36
  acc[resource.resourceId] = resource;
39
37
  return acc;
40
38
  }, {});
41
39
  config.value = resp.config;
42
- user.value = resp.user;
43
-
44
- // find homepage:true in menu recuresively
45
- if (import.meta.env.DEV){
46
- return
47
- } else {
48
- const homepage = await findHomepage(menu.value);
49
- if (homepage) {
50
- if (homepage.resourceId) {
51
- // redirect to homepage
52
- router.push({ name: 'resource-list', params: { resourceId: homepage.resourceId } });
53
- } else {
54
- // redirect to path
55
- router.push(homepage.path);
56
- }
57
- }
58
- }
40
+ adminUser.value = resp.user;
41
+ console.log('🌍 AdminForth v', resp.version);
59
42
  }
60
43
 
61
44
  async function fetchRecord({ resourceId, primaryKey }) {
62
45
  record.value = null;
46
+
47
+ if (!resource.value) {
48
+ throw new Error('Columns not fetched yet');
49
+ }
63
50
 
64
- record.value = await callAdminForthApi({
65
- path: '/get_record',
51
+ const respData = await callAdminForthApi({
52
+ path: '/get_resource_data',
66
53
  method: 'POST',
67
54
  body: {
55
+ source: 'show',
68
56
  resourceId: resourceId,
69
- primaryKey: primaryKey,
57
+ filters: [
58
+ {
59
+ field: resource.value.columns.find((col: AdminForthResourceColumn) => col.primaryKey).name,
60
+ operator: 'eq',
61
+ value: primaryKey
62
+ }
63
+ ],
64
+ sort: [],
65
+ limit: 1,
66
+ offset: 0
70
67
  }
71
68
  });
69
+
70
+ if (respData.error) {
71
+ window.adminforth.alert({
72
+ message: respData.error,
73
+ variant: 'danger',
74
+ timeout: 'unlimited'
75
+ });
76
+ record.value = {};
77
+ } else {
78
+ record.value = respData.data[0];
79
+ }
80
+
72
81
  }
73
82
 
74
- async function fetchColumns({ resourceId }) {
75
- if (resourceColumnsId.value === resourceId && resourceColumns.value) {
83
+ async function fetchResourceFull({ resourceId }: { resourceId: string }) {
84
+ if (resourceColumnsId.value === resourceId && resource.value) {
76
85
  // already fetched
77
86
  return;
78
87
  }
79
88
  resourceColumnsId.value = resourceId;
80
- resourceColumns.value = null;
81
89
  resourceColumnsError.value = '';
82
90
  const res = await callAdminForthApi({
83
- path: '/get_resource_columns',
91
+ path: '/get_resource',
84
92
  method: 'POST',
85
93
  body: {
86
94
  resourceId,
87
95
  }
88
- }
89
- );
96
+ });
90
97
  if (res.error) {
91
98
  resourceColumnsError.value = res.error;
92
99
  } else {
93
- resourceColumns.value = res.resource.columns;
100
+ resourceById.value[resourceId] = res.resource;
101
+ resource.value = res.resource;
102
+ resourceOptions.value = res.resource.options;
94
103
  }
95
104
  }
96
105
 
@@ -102,21 +111,16 @@ export const useCoreStore = defineStore('core', () => {
102
111
  config.value = {...config.value, ...res};
103
112
  }
104
113
 
105
- async function logout() {
106
- await callAdminForthApi({
107
- path: '/logout',
108
- method: 'POST',
109
- });
110
- }
114
+
111
115
 
112
116
  const username = computed(() => {
113
117
  const usernameField = config.value.usernameField;
114
- return user.value && user.value[usernameField];
118
+ return adminUser.value && adminUser.value[usernameField];
115
119
  });
116
120
 
117
121
  const userFullname = computed(() => {
118
122
  const userFullnameField = config.value.userFullnameField;
119
- return user.value && user.value[userFullnameField];
123
+ return adminUser.value && adminUser.value[userFullnameField];
120
124
  })
121
125
 
122
126
 
@@ -130,9 +134,11 @@ export const useCoreStore = defineStore('core', () => {
130
134
  fetchMenuAndResource,
131
135
  fetchRecord,
132
136
  record,
133
- resourceColumns,
134
- fetchColumns,
137
+ fetchResourceFull,
135
138
  resourceColumnsError,
136
- logout
139
+ resourceOptions,
140
+ resource,
141
+ adminUser,
142
+ resourceColumnsWithFilters
137
143
  }
138
144
  })
@@ -1,6 +1,12 @@
1
1
  import { ref } from 'vue'
2
2
  import { defineStore } from 'pinia'
3
- import { callAdminForthApi } from '@/utils';
3
+
4
+ type ModalContentType = {
5
+ title?: string;
6
+ content?: string;
7
+ acceptText?: string;
8
+ cancelText?: string;
9
+ }
4
10
 
5
11
 
6
12
  export const useModalStore = defineStore('modal', () => {
@@ -12,13 +18,17 @@ export const useModalStore = defineStore('modal', () => {
12
18
  });
13
19
  const isOpened = ref(false);
14
20
  const onAcceptFunction: any = ref(()=>{});
21
+ const onCancelFunction: any = ref(()=>{});
15
22
  function togleModal() {
16
23
  isOpened.value = !isOpened.value;
17
24
  }
18
25
  function setOnAcceptFunction(func: Function) {
19
26
  onAcceptFunction.value = func;
20
27
  }
21
- function setModalContent(content: string) {
28
+ function setOnCancelFunction(func: Function) {
29
+ onCancelFunction.value = func;
30
+ }
31
+ function setModalContent(content: ModalContentType) {
22
32
  modalContent.value = content;
23
33
  }
24
34
  function resetmodalState() {
@@ -33,6 +43,6 @@ export const useModalStore = defineStore('modal', () => {
33
43
 
34
44
  }
35
45
 
36
- return {isOpened, setModalContent, togleModal,modalContent, setOnAcceptFunction, onAcceptFunction,resetmodalState}
46
+ return {isOpened, setModalContent,onCancelFunction, togleModal,modalContent, setOnAcceptFunction, onAcceptFunction,resetmodalState,setOnCancelFunction}
37
47
 
38
48
  })
@@ -1,6 +1,10 @@
1
1
  import { onMounted, ref, resolveComponent } from 'vue';
2
+ import type { CoreConfig } from './spa_types/core';
2
3
 
3
4
  import router from "./router";
5
+ import { useRouter } from 'vue-router';
6
+ import { useCoreStore } from './stores/core';
7
+ import { useUserStore } from './stores/user';
4
8
 
5
9
  export async function callApi({path, method, body=undefined} ) {
6
10
  const options = {
@@ -13,14 +17,18 @@ export async function callApi({path, method, body=undefined} ) {
13
17
  const fullPath = `${import.meta.env.VITE_ADMINFORTH_PUBLIC_PATH || ''}${path}`;
14
18
  const r = await fetch(fullPath, options);
15
19
  if (r.status == 401) {
16
- console.log('router', router);
17
- router.push({name: 'login'});
20
+ useUserStore().unauthorize();
21
+ router.push({ name: 'login' });
18
22
  return null;
19
- }
23
+ }
20
24
  return await r.json();
21
25
  }
22
26
 
23
- export async function callAdminForthApi({ path, method, body=undefined }) {
27
+ export async function callAdminForthApi({ path, method, body=undefined }: {
28
+ path: string,
29
+ method: 'GET' | 'POST' | 'PUT' | 'DELETE',
30
+ body?: any
31
+ }) {
24
32
  try {
25
33
  return callApi({path: `/adminapi/v1${path}`, method, body} );
26
34
  } catch (e) {
@@ -29,9 +37,11 @@ export async function callAdminForthApi({ path, method, body=undefined }) {
29
37
  }
30
38
  }
31
39
 
32
-
33
-
34
-
40
+ export function getCustomComponent({ file, meta }: { file: string, meta: any }) {
41
+ const name = file.replace(/@/g, '').replace(/\./g, '').replace(/\//g, '');
42
+ console.log('resolving name', name);
43
+ return resolveComponent(name);
44
+ }
35
45
 
36
46
  export function getIcon(icon: string) {
37
47
  // icon format is "feather:icon-name". We need to get IconName in pascal case
@@ -41,4 +51,51 @@ export function getIcon(icon: string) {
41
51
  const [iconSet, iconName] = icon.split(':');
42
52
  const compName = 'Icon' + iconName.split('-').map((word) => word.charAt(0).toUpperCase() + word.slice(1)).join('');
43
53
  return resolveComponent(compName);
44
- }
54
+ }
55
+
56
+ export const loadFile = (file: string) => {
57
+ if (file.startsWith('http')) {
58
+ return file;
59
+ }
60
+ let path;
61
+ let baseUrl = '';
62
+ if (file.startsWith('@/')) {
63
+ path = file.replace('@/', '');
64
+ baseUrl = new URL(`./${path}`, import.meta.url).href;
65
+ } else if (file.startsWith('@@/')) {
66
+ path = file.replace('@@/', '');
67
+ baseUrl = new URL(`./custom/${path}`, import.meta.url).href;
68
+ } else {
69
+ baseUrl = new URL(`./${file}`, import.meta.url).href;
70
+ }
71
+ return baseUrl;
72
+ }
73
+
74
+ export function checkEmptyValues(value: any, viewType:'show' | 'list' ) {
75
+ const config: CoreConfig | {} = useCoreStore().config;
76
+ let emptyFieldPlaceholder = '';
77
+ if (config.emptyFieldPlaceholder) {
78
+ if(typeof config.emptyFieldPlaceholder === 'string') {
79
+ emptyFieldPlaceholder = config.emptyFieldPlaceholder;
80
+ } else {
81
+ emptyFieldPlaceholder = config.emptyFieldPlaceholder?.[viewType] || '';
82
+ }
83
+ if (value === null || value === undefined || value === '') {
84
+ return emptyFieldPlaceholder;
85
+ }
86
+ }
87
+ return value;
88
+ }
89
+
90
+ export function checkAcessByAllowedActions(allowedActions:any, action:any ) {
91
+ if (!allowedActions) {
92
+ console.warn('allowedActions not set');
93
+ return;
94
+ }
95
+ if(allowedActions[action] === false) {
96
+ console.warn(`Action ${action} is not allowed`);
97
+ router.back();
98
+ }
99
+ }
100
+
101
+
@@ -1,16 +1,25 @@
1
1
  <template>
2
2
  <div class="relative">
3
3
 
4
+ <component
5
+ v-for="c in coreStore?.resourceOptions?.pageInjections?.create?.beforeBreadcrumbs || []"
6
+ :is="getCustomComponent(c)"
7
+ :meta="c.meta"
8
+ :record="coreStore.record"
9
+ :resource="coreStore.resource"
10
+ :adminUser="coreStore.adminUser"
11
+ />
12
+
4
13
  <BreadcrumbsWithButtons>
5
14
  <!-- save and cancle -->
6
15
  <button @click="$router.back()"
7
- class="flex items-center py-1 px-3 me-2 mb-2 text-sm font-medium text-gray-900 focus:outline-none bg-white rounded border border-gray-300 hover:bg-gray-100 hover:text-blue-700 focus:z-10 focus:ring-4 focus:ring-gray-100 dark:focus:ring-gray-700 dark:bg-gray-800 dark:text-gray-400 dark:border-gray-600 dark:hover:text-white dark:hover:bg-gray-700"
16
+ class="flex items-center py-1 px-3 me-2 text-sm font-medium rounded-default text-gray-900 focus:outline-none bg-white rounded border border-gray-300 hover:bg-gray-100 hover:text-blue-700 focus:z-10 focus:ring-4 focus:ring-gray-100 dark:focus:ring-gray-700 dark:bg-gray-800 dark:text-gray-400 dark:border-gray-600 dark:hover:text-white dark:hover:bg-gray-700"
8
17
  >
9
18
  Cancel
10
19
  </button>
11
20
 
12
21
  <button @click="saveRecord"
13
- class="flex items-center py-1 px-3 mb-2 text-sm font-medium text-red-600 focus:outline-none bg-white rounded border border-gray-300 hover:bg-gray-100 hover:text-red-700 focus:z-10 focus:ring-4 focus:ring-gray-100 dark:focus:ring-gray-700 dark:bg-gray-800 dark:text-red-500 dark:border-gray-600 dark:hover:text-white dark:hover:bg-gray-700 disabled:opacity-50"
22
+ class="flex items-center py-1 px-3 text-sm font-medium rounded-default text-red-600 focus:outline-none bg-white rounded border border-gray-300 hover:bg-gray-100 hover:text-red-700 focus:z-10 focus:ring-4 focus:ring-gray-100 dark:focus:ring-gray-700 dark:bg-gray-800 dark:text-red-500 dark:border-gray-600 dark:hover:text-white dark:hover:bg-gray-700 disabled:opacity-50"
14
23
  :disabled="saving || (validating && !isValid)"
15
24
  >
16
25
  <svg v-if="saving" x
@@ -22,32 +31,55 @@
22
31
 
23
32
  </BreadcrumbsWithButtons>
24
33
 
34
+ <component
35
+ v-for="c in coreStore?.resourceOptions?.pageInjections?.create?.afterBreadcrumbs || []"
36
+ :is="getCustomComponent(c)"
37
+ :meta="c.meta"
38
+ :record="coreStore.record"
39
+ :resource="coreStore.resource"
40
+ :adminUser="coreStore.adminUser"
41
+ />
42
+
25
43
  <SingleSkeletLoader v-if="loading"></SingleSkeletLoader>
44
+
26
45
 
27
46
  <ResourceForm
28
47
  v-else
29
- :record="{}"
30
- :resourceColumns="coreStore.resourceColumns"
48
+ :record="record"
49
+ :resource="coreStore.resource"
31
50
  @update:record="onUpdateRecord"
32
51
  @update:isValid="isValid = $event"
33
52
  :validating="validating"
53
+ :customComponentsPerColumn="createComponentsPerColumn"
34
54
  >
35
55
  </ResourceForm>
36
56
 
57
+ <component
58
+ v-for="c in coreStore?.resourceOptions?.pageInjections?.create?.bottom || []"
59
+ :is="getCustomComponent(c)"
60
+ :meta="c.meta"
61
+ :record="coreStore.record"
62
+ :resource="coreStore.resource"
63
+ :adminUser="coreStore.adminUser"
64
+ />
65
+
37
66
  </div>
38
67
  </template>
39
68
 
40
69
 
41
70
  <script setup>
42
71
 
43
- import { ref, computed, onMounted } from 'vue';
44
- import { useRoute, useRouter } from 'vue-router';
45
- import { useCoreStore } from '@/stores/core';
46
- import { callAdminForthApi } from '@/utils';
47
- import ResourceForm from '@/components/ResourceForm.vue';
48
72
  import BreadcrumbsWithButtons from '@/components/BreadcrumbsWithButtons.vue';
49
- import { IconFloppyDiskSolid } from '@iconify-prerendered/vue-flowbite';
73
+ import ResourceForm from '@/components/ResourceForm.vue';
50
74
  import SingleSkeletLoader from '@/components/SingleSkeletLoader.vue';
75
+ import { useCoreStore } from '@/stores/core';
76
+ import { callAdminForthApi, getCustomComponent,checkAcessByAllowedActions } from '@/utils';
77
+ import { IconFloppyDiskSolid } from '@iconify-prerendered/vue-flowbite';
78
+ import { onMounted, ref, watch } from 'vue';
79
+ import { useRoute, useRouter } from 'vue-router';
80
+ import { computed } from 'vue';
81
+ import { showErrorTost } from '@/composables/useFrontendApi';
82
+
51
83
 
52
84
  const isValid = ref(false);
53
85
  const validating = ref(false);
@@ -59,9 +91,19 @@ const route = useRoute();
59
91
  const router = useRouter();
60
92
 
61
93
  const record = ref({});
94
+ let createComponentsPerColumn = {};
62
95
 
63
96
  const coreStore = useCoreStore();
64
97
 
98
+ const initalValues = computed(() => {
99
+ if (!route.query.values) {
100
+ return {};
101
+ }
102
+ return JSON.parse(decodeURIComponent(route.query.values));
103
+ });
104
+
105
+
106
+
65
107
  async function onUpdateRecord(newRecord) {
66
108
  console.log('newRecord', newRecord);
67
109
  record.value = newRecord;
@@ -69,10 +111,18 @@ async function onUpdateRecord(newRecord) {
69
111
 
70
112
  onMounted(async () => {
71
113
  loading.value = true;
72
- coreStore.fetchColumns({
114
+ record.value = initalValues.value;
115
+ await coreStore.fetchResourceFull({
73
116
  resourceId: route.params.resourceId
74
117
  });
118
+ createComponentsPerColumn = coreStore.resource.columns.reduce((acc, column) => {
119
+ if (column.components?.create) {
120
+ acc[column.name] = getCustomComponent(column.components.create);
121
+ }
122
+ return acc;
123
+ }, {});
75
124
  loading.value = false;
125
+ checkAcessByAllowedActions(coreStore.resourceOptions.allowedActions,'create');
76
126
  });
77
127
 
78
128
  async function saveRecord() {
@@ -91,12 +141,22 @@ async function saveRecord() {
91
141
  record: record.value,
92
142
  },
93
143
  });
144
+ if (response.error) {
145
+ showErrorTost(response.error);
146
+
147
+ }
94
148
  saving.value = false;
95
- router.push({ name: 'resource-show', params: {
96
- resourceId: route.params.resourceId,
97
- primaryKey: response.newRecordId
98
-
99
- } });
149
+ if (route.query.returnTo) {
150
+ router.push(route.query.returnTo);
151
+ } else {
152
+ router.push({
153
+ name: 'resource-show',
154
+ params: {
155
+ resourceId: route.params.resourceId,
156
+ primaryKey: response.newRecordId
157
+ }
158
+ });
159
+ }
100
160
  }
101
161
 
102
162